From d29dea982dee1b7a1f5ffec2a79fb1e45692887d Mon Sep 17 00:00:00 2001 From: Dhia Bouassida <165925972+sfc-gh-dbouassida@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:14:46 +0200 Subject: [PATCH 01/53] SNOW-1501662 Allow JDBC to handle ZSTD compressed http response streams (#1802) This PR introduces the integration of the zstd-jni library into Snowflake JDBC. --- FIPS/pom.xml | 4 + FIPS/scripts/check_content.sh | 4 +- ci/scripts/check_content.sh | 6 +- linkage-checker-exclusion-rules.xml | 5 - parent-pom.xml | 10 ++ pom.xml | 4 + .../jdbc/DefaultResultStreamProvider.java | 3 + .../jdbc/DefaultResultStreamProviderTest.java | 120 ++++++++++++++++++ thin_public_pom.xml | 6 + 9 files changed, 152 insertions(+), 10 deletions(-) create mode 100644 src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 04fa6a5f5..dd994a297 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -429,6 +429,10 @@ org.jsoup ${shadeBase}.org.jsoup + + com.github.luben.zstd + ${shadeBase}.com.github.luben.zstd + com.nimbusds ${shadeBase}.com.nimbusds diff --git a/FIPS/scripts/check_content.sh b/FIPS/scripts/check_content.sh index 8b818b1b4..fa675655e 100755 --- a/FIPS/scripts/check_content.sh +++ b/FIPS/scripts/check_content.sh @@ -1,12 +1,12 @@ #!/bin/bash -e -# scripts used to check if all dependency is shaded into snowflake internal path +# scripts used to check if all dependencies are shaded into snowflake internal path set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types; then +if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi diff --git a/ci/scripts/check_content.sh b/ci/scripts/check_content.sh index a9c0768b6..ce6f34180 100755 --- a/ci/scripts/check_content.sh +++ b/ci/scripts/check_content.sh @@ -8,12 +8,12 @@ set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types; then +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake"; then - echo "[ERROR] JDBC jar includes multi release classes not under the snowflake namespace" +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then + echo "[ERROR] JDBC jar includes multi-release classes not under the snowflake namespace" exit 1 fi diff --git a/linkage-checker-exclusion-rules.xml b/linkage-checker-exclusion-rules.xml index 8bad89714..0f6275008 100644 --- a/linkage-checker-exclusion-rules.xml +++ b/linkage-checker-exclusion-rules.xml @@ -19,11 +19,6 @@ Optional - - - - Optional - diff --git a/parent-pom.xml b/parent-pom.xml index 28412d161..04dc541e0 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -19,6 +19,7 @@ 1.10.0 4.5.14 4.4.16 + 1.5.6-5 17.0.0 9.3 1.8.1 @@ -327,6 +328,11 @@ httpcore ${apache.httpcore.version} + + com.github.luben + zstd-jni + ${zstd-jni.version} + org.apache.tika tika-core @@ -644,6 +650,10 @@ org.apache.httpcomponents httpcore + + com.github.luben + zstd-jni + org.apache.tika tika-core diff --git a/pom.xml b/pom.xml index 096641174..e41cb7f62 100644 --- a/pom.xml +++ b/pom.xml @@ -947,6 +947,10 @@ android.annotation ${shadeBase}.android.annotation + + com.github.luben.zstd + ${shadeBase}.com.github.luben.zstd + diff --git a/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java b/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java index 3ee556bb4..567db8fa1 100644 --- a/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java +++ b/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java @@ -2,6 +2,7 @@ import static net.snowflake.client.core.Constants.MB; +import com.github.luben.zstd.ZstdInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; @@ -153,6 +154,8 @@ private InputStream detectContentEncodingAndGetInputStream(HttpResponse response if ("gzip".equalsIgnoreCase(encoding.getValue())) { /* specify buffer size for GZIPInputStream */ inputStream = new GZIPInputStream(is, STREAM_BUFFER_SIZE); + } else if ("zstd".equalsIgnoreCase(encoding.getValue())) { + inputStream = new ZstdInputStream(is); } else { throw new SnowflakeSQLException( SqlState.INTERNAL_ERROR, diff --git a/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java b/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java new file mode 100644 index 000000000..b78bf3a5e --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java @@ -0,0 +1,120 @@ +package net.snowflake.client.jdbc; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.github.luben.zstd.ZstdInputStream; +import com.github.luben.zstd.ZstdOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.junit.Before; +import org.junit.Test; + +public class DefaultResultStreamProviderTest { + + private DefaultResultStreamProvider resultStreamProvider; + private HttpResponse mockResponse; + + @Before + public void setUp() { + resultStreamProvider = new DefaultResultStreamProvider(); + mockResponse = mock(HttpResponse.class); + } + + private InputStream invokeDetectContentEncodingAndGetInputStream( + HttpResponse response, InputStream inputStream) throws Exception { + Method method = + DefaultResultStreamProvider.class.getDeclaredMethod( + "detectContentEncodingAndGetInputStream", HttpResponse.class, InputStream.class); + method.setAccessible(true); + return (InputStream) method.invoke(resultStreamProvider, response, inputStream); + } + + @Test + public void testDetectContentEncodingAndGetInputStream_Gzip() throws Exception { + // Mocking gzip content encoding + Header encodingHeader = mock(Header.class); + when(encodingHeader.getValue()).thenReturn("gzip"); + when(mockResponse.getFirstHeader("Content-Encoding")).thenReturn(encodingHeader); + + // Original data to compress and validate + String originalData = "Some data in GZIP"; + + // Creating a gzip byte array using GZIPOutputStream + byte[] gzipData; + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) { + gzipOutputStream.write(originalData.getBytes(StandardCharsets.UTF_8)); + gzipOutputStream.close(); // close to flush and finish the compression + gzipData = byteArrayOutputStream.toByteArray(); + } + + // Mocking input stream with the gzip data + InputStream gzipStream = new ByteArrayInputStream(gzipData); + + // Call the private method using reflection + InputStream resultStream = + invokeDetectContentEncodingAndGetInputStream(mockResponse, gzipStream); + + // Decompress and validate the data matches original + ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + try (GZIPInputStream gzipInputStream = (GZIPInputStream) resultStream) { + while ((bytesRead = gzipInputStream.read(buffer)) != -1) { + decompressedOutput.write(buffer, 0, bytesRead); + } + } + String decompressedData = new String(decompressedOutput.toByteArray(), StandardCharsets.UTF_8); + + assertEquals(originalData, decompressedData); + } + + @Test + public void testDetectContentEncodingAndGetInputStream_Zstd() throws Exception { + // Mocking zstd content encoding + Header encodingHeader = mock(Header.class); + when(encodingHeader.getValue()).thenReturn("zstd"); + when(mockResponse.getFirstHeader("Content-Encoding")).thenReturn(encodingHeader); + + // Original data to compress and validate + String originalData = "Some data in ZSTD"; + + // Creating a zstd byte array using ZstdOutputStream + byte[] zstdData; + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZstdOutputStream zstdOutputStream = new ZstdOutputStream(byteArrayOutputStream)) { + zstdOutputStream.write(originalData.getBytes(StandardCharsets.UTF_8)); + zstdOutputStream.close(); // close to flush and finish the compression + zstdData = byteArrayOutputStream.toByteArray(); + } + + // Mocking input stream with the zstd data + InputStream zstdStream = new ByteArrayInputStream(zstdData); + + // Call the private method using reflection + InputStream resultStream = + invokeDetectContentEncodingAndGetInputStream(mockResponse, zstdStream); + + // Decompress and validate the data matches original + ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + try (ZstdInputStream zstdInputStream = (ZstdInputStream) resultStream) { + while ((bytesRead = zstdInputStream.read(buffer)) != -1) { + decompressedOutput.write(buffer, 0, bytesRead); + } + } + String decompressedData = new String(decompressedOutput.toByteArray(), StandardCharsets.UTF_8); + + assertEquals(originalData, decompressedData); + } +} diff --git a/thin_public_pom.xml b/thin_public_pom.xml index 31a1aedee..a781a376d 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -64,6 +64,7 @@ UTF-8 2.0.13 1.6.9 + 1.5.6-5 @@ -262,6 +263,11 @@ jsoup ${jsoup.version} + + com.github.luben + zstd-jni + ${zstd-jni.version} + org.slf4j slf4j-api From 9da30dee40fd82cff27d6d03bc97615ded03ee46 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Thu, 12 Sep 2024 09:30:52 +0200 Subject: [PATCH 02/53] Prepare next development version 3.19.1-SNAPSHOT (#1890) --- FIPS/pom.xml | 4 ++-- parent-pom.xml | 2 +- pom.xml | 4 ++-- src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index dd994a297..9803c4db7 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -5,12 +5,12 @@ net.snowflake snowflake-jdbc-parent - 3.19.0 + 3.19.1-SNAPSHOT ../parent-pom.xml snowflake-jdbc-fips - 3.19.0 + 3.19.1-SNAPSHOT jar snowflake-jdbc-fips diff --git a/parent-pom.xml b/parent-pom.xml index 04dc541e0..ab6948da5 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -5,7 +5,7 @@ net.snowflake snowflake-jdbc-parent - 3.19.0 + 3.19.1-SNAPSHOT pom diff --git a/pom.xml b/pom.xml index e41cb7f62..bf917e1a4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ net.snowflake snowflake-jdbc-parent - 3.19.0 + 3.19.1-SNAPSHOT ./parent-pom.xml ${artifactId} - 3.19.0 + 3.19.1-SNAPSHOT jar ${artifactId} diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java index 05566da82..060ac977e 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java @@ -37,7 +37,7 @@ public class SnowflakeDriver implements Driver { static SnowflakeDriver INSTANCE; public static final Properties EMPTY_PROPERTIES = new Properties(); - public static String implementVersion = "3.19.0"; + public static String implementVersion = "3.19.1"; static int majorVersion = 0; static int minorVersion = 0; From 959757606514a9c49ef8c3a690d21a3542fc61c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Tue, 24 Sep 2024 12:23:22 +0200 Subject: [PATCH 03/53] SNOW-1668681: Run JDBC unit tests on AIX (#1900) --- Jenkinsfile | 2 ++ .../snowflake/client/core/arrow/BaseConverterTest.java | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 8e5925b8c..261a2968b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -87,6 +87,8 @@ timestamps { }.collectEntries { jobDefinition -> return [(jobDefinition.runName): { build job: jobDefinition.jobToRun, parameters: jobDefinition.params }] } + + jobDefinitions.put('JDBC-AIX-Unit', { build job: 'JDBC-AIX-UnitTests', parameters: [ string(name: 'BRANCH', value: scmInfo.GIT_BRANCH ) ] } ) stage('Test') { parallel (jobDefinitions) } diff --git a/src/test/java/net/snowflake/client/core/arrow/BaseConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BaseConverterTest.java index 20a07a655..e669ac006 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BaseConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BaseConverterTest.java @@ -3,6 +3,7 @@ */ package net.snowflake.client.core.arrow; +import java.nio.ByteOrder; import java.util.TimeZone; import net.snowflake.client.core.DataConversionContext; import net.snowflake.client.core.SFSession; @@ -10,6 +11,8 @@ import net.snowflake.common.core.SFBinaryFormat; import net.snowflake.common.core.SnowflakeDateTimeFormat; import org.junit.After; +import org.junit.Assume; +import org.junit.Before; public class BaseConverterTest implements DataConversionContext { private SnowflakeDateTimeFormat dateTimeFormat = @@ -32,6 +35,13 @@ public void clearTimeZone() { System.clearProperty("user.timezone"); } + @Before + public void assumeLittleEndian() { + Assume.assumeTrue( + "Arrow doesn't support cross endianness", + ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)); + } + @Override public SnowflakeDateTimeFormat getTimestampLTZFormatter() { return timestampLTZFormat; From 9e221ea891c766810927078bb808c460b873d800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kubik?= Date: Fri, 27 Sep 2024 17:24:28 +0200 Subject: [PATCH 04/53] SNOW-1374896 unify structured types string representation (#1882) Co-authored-by: sfc-gh-astachowski Build string representations of Snowflake structured types recursively to reuse existing converters designed for specific logical types (e.g. timestamps/binary) https://snowflakecomputing.atlassian.net/browse/SNOW-1374896 --- .../net/snowflake/client/core/ResultUtil.java | 2 +- .../client/core/arrow/ArrayConverter.java | 24 +++- .../core/arrow/ArrowVectorConverterUtil.java | 22 ++- .../core/arrow/BitToBooleanConverter.java | 2 +- .../client/core/arrow/MapConverter.java | 29 +++- .../client/core/arrow/StructConverter.java | 18 ++- ...ArrowArrayStringRepresentationBuilder.java | 19 +++ ...rrowObjectStringRepresentationBuilder.java | 21 +++ .../ArrowStringRepresentationBuilderBase.java | 53 +++++++ .../java/net/snowflake/client/TestUtil.java | 10 ++ .../core/arrow/BitToBooleanConverterTest.java | 2 +- .../client/core/json/StringConverterTest.java | 8 +- .../client/jdbc/ResultSetJsonVsArrowIT.java | 4 +- .../client/jdbc/ResultSetLatestIT.java | 4 +- .../ResultSetStructuredTypesLatestIT.java | 94 +++--------- ...ypesGetStringArrowJsonCompatibilityIT.java | 135 ++++++++++++++++++ .../StructuredTypesGetStringBaseIT.java | 68 +++++++++ .../sqldata/AllTypesClass.java | 37 +++++ 18 files changed, 459 insertions(+), 93 deletions(-) create mode 100644 src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowArrayStringRepresentationBuilder.java create mode 100644 src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowObjectStringRepresentationBuilder.java create mode 100644 src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowStringRepresentationBuilderBase.java create mode 100644 src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java create mode 100644 src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringBaseIT.java diff --git a/src/main/java/net/snowflake/client/core/ResultUtil.java b/src/main/java/net/snowflake/client/core/ResultUtil.java index b894f4259..6c00f513e 100644 --- a/src/main/java/net/snowflake/client/core/ResultUtil.java +++ b/src/main/java/net/snowflake/client/core/ResultUtil.java @@ -251,7 +251,7 @@ public static String getSFTimeAsString( * @return boolean in string */ public static String getBooleanAsString(boolean bool) { - return bool ? "TRUE" : "FALSE"; + return bool ? "true" : "false"; } /** diff --git a/src/main/java/net/snowflake/client/core/arrow/ArrayConverter.java b/src/main/java/net/snowflake/client/core/arrow/ArrayConverter.java index 08ce23eec..48b8fa083 100644 --- a/src/main/java/net/snowflake/client/core/arrow/ArrayConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/ArrayConverter.java @@ -2,7 +2,10 @@ import net.snowflake.client.core.DataConversionContext; import net.snowflake.client.core.SFException; +import net.snowflake.client.core.arrow.tostringhelpers.ArrowArrayStringRepresentationBuilder; +import net.snowflake.client.jdbc.SnowflakeSQLException; import net.snowflake.client.jdbc.SnowflakeType; +import org.apache.arrow.vector.FieldVector; import org.apache.arrow.vector.complex.ListVector; public class ArrayConverter extends AbstractArrowVectorConverter { @@ -21,6 +24,25 @@ public Object toObject(int index) throws SFException { @Override public String toString(int index) throws SFException { - return vector.getObject(index).toString(); + FieldVector vectorUnpacked = vector.getChildrenFromFields().get(0); + SnowflakeType logicalType = + ArrowVectorConverterUtil.getSnowflakeTypeFromFieldMetadata(vectorUnpacked.getField()); + + ArrowArrayStringRepresentationBuilder builder = + new ArrowArrayStringRepresentationBuilder(logicalType); + + final ArrowVectorConverter converter; + + try { + converter = ArrowVectorConverterUtil.initConverter(vectorUnpacked, context, columnIndex); + } catch (SnowflakeSQLException e) { + return vector.getObject(index).toString(); + } + + for (int i = vector.getElementStartIndex(index); i < vector.getElementEndIndex(index); i++) { + builder.appendValue(converter.toString(i)); + } + + return builder.toString(); } } diff --git a/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverterUtil.java b/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverterUtil.java index 1aa84db8f..0231ebd51 100644 --- a/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverterUtil.java +++ b/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverterUtil.java @@ -9,17 +9,28 @@ import net.snowflake.client.jdbc.SnowflakeSQLLoggedException; import net.snowflake.client.jdbc.SnowflakeType; import net.snowflake.common.core.SqlState; +import org.apache.arrow.vector.FieldVector; import org.apache.arrow.vector.ValueVector; import org.apache.arrow.vector.complex.FixedSizeListVector; import org.apache.arrow.vector.complex.ListVector; import org.apache.arrow.vector.complex.MapVector; import org.apache.arrow.vector.complex.StructVector; import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; @SnowflakeJdbcInternalApi public final class ArrowVectorConverterUtil { private ArrowVectorConverterUtil() {} + public static SnowflakeType getSnowflakeTypeFromFieldMetadata(Field field) { + Map customMeta = field.getMetadata(); + if (customMeta != null && customMeta.containsKey("logicalType")) { + return SnowflakeType.valueOf(customMeta.get("logicalType")); + } + + return null; + } + /** * Given an arrow vector (a single column in a single record batch), return an arrow vector * converter. Note, converter is built on top of arrow vector, so that arrow data can be converted @@ -51,12 +62,11 @@ public static ArrowVectorConverter initConverter( Types.MinorType type = Types.getMinorTypeForArrowType(vector.getField().getType()); // each column's metadata - Map customMeta = vector.getField().getMetadata(); + SnowflakeType st = getSnowflakeTypeFromFieldMetadata(vector.getField()); if (type == Types.MinorType.DECIMAL) { // Note: Decimal vector is different from others return new DecimalToScaledFixedConverter(vector, idx, context); - } else if (!customMeta.isEmpty()) { - SnowflakeType st = SnowflakeType.valueOf(customMeta.get("logicalType")); + } else if (st != null) { switch (st) { case ANY: case CHAR: @@ -216,4 +226,10 @@ public static ArrowVectorConverter initConverter( "Unexpected Arrow Field for ", type.toString()); } + + public static ArrowVectorConverter initConverter( + FieldVector vector, DataConversionContext context, int columnIndex) + throws SnowflakeSQLException { + return initConverter(vector, context, context.getSession(), columnIndex); + } } diff --git a/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java b/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java index 2f5a8cf83..640ff68ca 100644 --- a/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java @@ -58,7 +58,7 @@ public Object toObject(int index) { @Override public String toString(int index) { - return isNull(index) ? null : toBoolean(index) ? "TRUE" : "FALSE"; + return isNull(index) ? null : toBoolean(index) ? "true" : "false"; } @Override diff --git a/src/main/java/net/snowflake/client/core/arrow/MapConverter.java b/src/main/java/net/snowflake/client/core/arrow/MapConverter.java index 433792294..8ef1cdccf 100644 --- a/src/main/java/net/snowflake/client/core/arrow/MapConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/MapConverter.java @@ -4,7 +4,10 @@ import java.util.stream.Collectors; import net.snowflake.client.core.DataConversionContext; import net.snowflake.client.core.SFException; +import net.snowflake.client.core.arrow.tostringhelpers.ArrowObjectStringRepresentationBuilder; +import net.snowflake.client.jdbc.SnowflakeSQLException; import net.snowflake.client.jdbc.SnowflakeType; +import org.apache.arrow.vector.FieldVector; import org.apache.arrow.vector.complex.MapVector; import org.apache.arrow.vector.util.JsonStringHashMap; @@ -28,6 +31,30 @@ public Object toObject(int index) throws SFException { @Override public String toString(int index) throws SFException { - return vector.getObject(index).toString(); + ArrowObjectStringRepresentationBuilder builder = new ArrowObjectStringRepresentationBuilder(); + + FieldVector vectorUnpacked = vector.getChildrenFromFields().get(0); + + FieldVector keys = vectorUnpacked.getChildrenFromFields().get(0); + FieldVector values = vectorUnpacked.getChildrenFromFields().get(1); + final ArrowVectorConverter keyConverter; + final ArrowVectorConverter valueConverter; + + SnowflakeType valueLogicalType = + ArrowVectorConverterUtil.getSnowflakeTypeFromFieldMetadata(values.getField()); + + try { + keyConverter = ArrowVectorConverterUtil.initConverter(keys, context, columnIndex); + valueConverter = ArrowVectorConverterUtil.initConverter(values, context, columnIndex); + } catch (SnowflakeSQLException e) { + return vector.getObject(index).toString(); + } + + for (int i = vector.getElementStartIndex(index); i < vector.getElementEndIndex(index); i++) { + builder.appendKeyValue( + keyConverter.toString(i), valueConverter.toString(i), valueLogicalType); + } + + return builder.toString(); } } diff --git a/src/main/java/net/snowflake/client/core/arrow/StructConverter.java b/src/main/java/net/snowflake/client/core/arrow/StructConverter.java index 84ccd7c0f..4c0516c51 100644 --- a/src/main/java/net/snowflake/client/core/arrow/StructConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/StructConverter.java @@ -3,7 +3,10 @@ import net.snowflake.client.core.DataConversionContext; import net.snowflake.client.core.SFException; import net.snowflake.client.core.SnowflakeJdbcInternalApi; +import net.snowflake.client.core.arrow.tostringhelpers.ArrowObjectStringRepresentationBuilder; +import net.snowflake.client.jdbc.SnowflakeSQLException; import net.snowflake.client.jdbc.SnowflakeType; +import org.apache.arrow.vector.FieldVector; import org.apache.arrow.vector.complex.StructVector; @SnowflakeJdbcInternalApi @@ -23,6 +26,19 @@ public Object toObject(int index) throws SFException { @Override public String toString(int index) throws SFException { - return structVector.getObject(index).toString(); + ArrowObjectStringRepresentationBuilder builder = new ArrowObjectStringRepresentationBuilder(); + for (String childName : structVector.getChildFieldNames()) { + FieldVector fieldVector = structVector.getChild(childName); + SnowflakeType logicalType = + ArrowVectorConverterUtil.getSnowflakeTypeFromFieldMetadata(fieldVector.getField()); + try { + ArrowVectorConverter converter = + ArrowVectorConverterUtil.initConverter(fieldVector, context, columnIndex); + builder.appendKeyValue(childName, converter.toString(index), logicalType); + } catch (SnowflakeSQLException e) { + return structVector.getObject(index).toString(); + } + } + return builder.toString(); } } diff --git a/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowArrayStringRepresentationBuilder.java b/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowArrayStringRepresentationBuilder.java new file mode 100644 index 000000000..7ee6a07aa --- /dev/null +++ b/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowArrayStringRepresentationBuilder.java @@ -0,0 +1,19 @@ +package net.snowflake.client.core.arrow.tostringhelpers; + +import net.snowflake.client.core.SnowflakeJdbcInternalApi; +import net.snowflake.client.jdbc.SnowflakeType; + +@SnowflakeJdbcInternalApi +public class ArrowArrayStringRepresentationBuilder extends ArrowStringRepresentationBuilderBase { + + private final SnowflakeType valueType; + + public ArrowArrayStringRepresentationBuilder(SnowflakeType valueType) { + super(",", "[", "]"); + this.valueType = valueType; + } + + public ArrowStringRepresentationBuilderBase appendValue(String value) { + return add(quoteIfNeeded(value, valueType)); + } +} diff --git a/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowObjectStringRepresentationBuilder.java b/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowObjectStringRepresentationBuilder.java new file mode 100644 index 000000000..53513836b --- /dev/null +++ b/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowObjectStringRepresentationBuilder.java @@ -0,0 +1,21 @@ +package net.snowflake.client.core.arrow.tostringhelpers; + +import java.util.StringJoiner; +import net.snowflake.client.core.SnowflakeJdbcInternalApi; +import net.snowflake.client.jdbc.SnowflakeType; + +@SnowflakeJdbcInternalApi +public class ArrowObjectStringRepresentationBuilder extends ArrowStringRepresentationBuilderBase { + + public ArrowObjectStringRepresentationBuilder() { + super(",", "{", "}"); + } + + public ArrowStringRepresentationBuilderBase appendKeyValue( + String key, String value, SnowflakeType valueType) { + StringJoiner joiner = new StringJoiner(": "); + joiner.add('"' + key + '"'); + joiner.add(quoteIfNeeded(value, valueType)); + return add(joiner.toString()); + } +} diff --git a/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowStringRepresentationBuilderBase.java b/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowStringRepresentationBuilderBase.java new file mode 100644 index 000000000..5b83f4497 --- /dev/null +++ b/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowStringRepresentationBuilderBase.java @@ -0,0 +1,53 @@ +package net.snowflake.client.core.arrow.tostringhelpers; + +import java.util.HashSet; +import java.util.Set; +import java.util.StringJoiner; +import net.snowflake.client.core.SnowflakeJdbcInternalApi; +import net.snowflake.client.jdbc.SnowflakeType; + +@SnowflakeJdbcInternalApi +public abstract class ArrowStringRepresentationBuilderBase { + private final StringJoiner joiner; + private static final Set quotableTypes; + + static { + quotableTypes = new HashSet<>(); + quotableTypes.add(SnowflakeType.ANY); + quotableTypes.add(SnowflakeType.CHAR); + quotableTypes.add(SnowflakeType.TEXT); + quotableTypes.add(SnowflakeType.VARIANT); + quotableTypes.add(SnowflakeType.BINARY); + quotableTypes.add(SnowflakeType.DATE); + quotableTypes.add(SnowflakeType.TIME); + quotableTypes.add(SnowflakeType.TIMESTAMP_LTZ); + quotableTypes.add(SnowflakeType.TIMESTAMP_NTZ); + quotableTypes.add(SnowflakeType.TIMESTAMP_TZ); + } + + public ArrowStringRepresentationBuilderBase(String delimiter, String prefix, String suffix) { + joiner = new StringJoiner(delimiter, prefix, suffix); + } + + protected ArrowStringRepresentationBuilderBase add(String string) { + joiner.add(string); + return this; + } + + private boolean shouldQuoteValue(SnowflakeType type) { + return quotableTypes.contains(type); + } + + protected String quoteIfNeeded(String string, SnowflakeType type) { + if (shouldQuoteValue(type)) { + return '"' + string + '"'; + } + + return string; + } + + @Override + public String toString() { + return joiner.toString(); + } +} diff --git a/src/test/java/net/snowflake/client/TestUtil.java b/src/test/java/net/snowflake/client/TestUtil.java index 76487bcb4..ba73dbb01 100644 --- a/src/test/java/net/snowflake/client/TestUtil.java +++ b/src/test/java/net/snowflake/client/TestUtil.java @@ -144,4 +144,14 @@ public static void expectSnowflakeLoggedFeatureNotSupportedException(MethodRaise assertEquals(ex.getClass().getSimpleName(), "SnowflakeLoggedFeatureNotSupportedException"); } } + + /** + * Compares two string values both values are cleaned of whitespaces + * + * @param expected expected value + * @param actual actual value + */ + public static void assertEqualsIgnoringWhitespace(String expected, String actual) { + assertEquals(expected.replaceAll("\\s+", ""), actual.replaceAll("\\s+", "")); + } } diff --git a/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java index e5091d6fc..1fd65d911 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java @@ -73,7 +73,7 @@ public void testConvertToString() throws SFException { } else { assertThat(boolVal, is(expectedValues.get(i))); assertThat(objectVal, is(expectedValues.get(i))); - assertThat(stringVal, is(expectedValues.get(i).toString().toUpperCase())); + assertThat(stringVal, is(expectedValues.get(i).toString())); if (boolVal) { assertThat((byte) 0x1, is(converter.toBytes(i)[0])); } else { diff --git a/src/test/java/net/snowflake/client/core/json/StringConverterTest.java b/src/test/java/net/snowflake/client/core/json/StringConverterTest.java index 5fe3dd2cb..748548966 100644 --- a/src/test/java/net/snowflake/client/core/json/StringConverterTest.java +++ b/src/test/java/net/snowflake/client/core/json/StringConverterTest.java @@ -59,10 +59,10 @@ public void testConvertingString() throws SFException { @Test public void testConvertingBoolean() throws SFException { - assertEquals("TRUE", stringConverter.getString(true, Types.BOOLEAN, Types.BOOLEAN, 0)); - assertEquals("TRUE", stringConverter.getString("true", Types.BOOLEAN, Types.BOOLEAN, 0)); - assertEquals("FALSE", stringConverter.getString(false, Types.BOOLEAN, Types.BOOLEAN, 0)); - assertEquals("FALSE", stringConverter.getString("false", Types.BOOLEAN, Types.BOOLEAN, 0)); + assertEquals("true", stringConverter.getString(true, Types.BOOLEAN, Types.BOOLEAN, 0)); + assertEquals("true", stringConverter.getString("true", Types.BOOLEAN, Types.BOOLEAN, 0)); + assertEquals("false", stringConverter.getString(false, Types.BOOLEAN, Types.BOOLEAN, 0)); + assertEquals("false", stringConverter.getString("false", Types.BOOLEAN, Types.BOOLEAN, 0)); } @Test diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java index 65cc27242..0dd8edd47 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java @@ -1501,12 +1501,12 @@ public void testBoolean() throws SQLException { ResultSet rs = statement.executeQuery("select * from " + table)) { assertTrue(rs.next()); assertTrue(rs.getBoolean(1)); - assertEquals("TRUE", rs.getString(1)); + assertEquals("true", rs.getString(1)); assertTrue(rs.next()); assertFalse(rs.getBoolean(1)); assertTrue(rs.next()); assertFalse(rs.getBoolean(1)); - assertEquals("FALSE", rs.getString(1)); + assertEquals("false", rs.getString(1)); assertFalse(rs.next()); statement.execute("drop table if exists " + table); } diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java index d82cd9ff2..3ad105cea 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java @@ -1176,9 +1176,9 @@ public void testGetObjectWithType() throws SQLException { assertResultValueAndType(statement, Double.valueOf("1.1"), "f", Double.class); assertResultValueAndType(statement, Double.valueOf("2.2"), "d", Double.class); assertResultValueAndType(statement, BigDecimal.valueOf(3.3), "bd", BigDecimal.class); - assertResultValueAndType(statement, "FALSE", "bool", String.class); + assertResultValueAndType(statement, "false", "bool", String.class); assertResultValueAndType(statement, Boolean.FALSE, "bool", Boolean.class); - assertResultValueAndType(statement, Long.valueOf(0), "bool", Long.class); + assertResultValueAndType(statement, 0L, "bool", Long.class); assertResultValueAsString( statement, new SnowflakeTimestampWithTimezone( diff --git a/src/test/java/net/snowflake/client/jdbc/structuredtypes/ResultSetStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/structuredtypes/ResultSetStructuredTypesLatestIT.java index b1da95b99..2857634f8 100644 --- a/src/test/java/net/snowflake/client/jdbc/structuredtypes/ResultSetStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/structuredtypes/ResultSetStructuredTypesLatestIT.java @@ -26,6 +26,7 @@ import java.util.Map; import net.snowflake.client.ConditionalIgnoreRule; import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.TestUtil; import net.snowflake.client.ThrowingConsumer; import net.snowflake.client.category.TestCategoryResultSet; import net.snowflake.client.core.structs.SnowflakeObjectTypeFactories; @@ -144,43 +145,7 @@ public void testMapStructAllTypes() throws SQLException { try (Connection connection = init(); Statement statement = connection.createStatement()) { statement.execute("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'"); - try (ResultSet resultSet = - statement.executeQuery( - "select {" - + "'string': 'a', " - + "'b': 1, " - + "'s': 2, " - + "'i': 3, " - + "'l': 4, " - + "'f': 1.1, " - + "'d': 2.2, " - + "'bd': 3.3, " - + "'bool': true, " - + "'timestamp_ltz': '2021-12-22 09:43:44'::TIMESTAMP_LTZ, " - + "'timestamp_ntz': '2021-12-23 09:44:44'::TIMESTAMP_NTZ, " - + "'timestamp_tz': '2021-12-24 09:45:45 +0800'::TIMESTAMP_TZ, " - + "'date': '2023-12-24'::DATE, " - + "'time': '12:34:56'::TIME, " - + "'binary': TO_BINARY('616263', 'HEX'), " - + "'simpleClass': {'string': 'b', 'intValue': 2}" - + "}::OBJECT(" - + "string VARCHAR, " - + "b TINYINT, " - + "s SMALLINT, " - + "i INTEGER, " - + "l BIGINT, " - + "f FLOAT, " - + "d DOUBLE, " - + "bd DOUBLE, " - + "bool BOOLEAN, " - + "timestamp_ltz TIMESTAMP_LTZ, " - + "timestamp_ntz TIMESTAMP_NTZ, " - + "timestamp_tz TIMESTAMP_TZ, " - + "date DATE, " - + "time TIME, " - + "binary BINARY, " - + "simpleClass OBJECT(string VARCHAR, intValue INTEGER)" - + ")"); ) { + try (ResultSet resultSet = statement.executeQuery(AllTypesClass.ALL_TYPES_QUERY); ) { resultSet.next(); AllTypesClass object = resultSet.getObject(1, AllTypesClass.class); assertEquals("a", object.getString()); @@ -213,6 +178,14 @@ public void testMapStructAllTypes() throws SQLException { assertTrue(object.getBool()); assertEquals("b", object.getSimpleClass().getString()); assertEquals(Integer.valueOf(2), object.getSimpleClass().getIntValue()); + + if (queryResultFormat == ResultSetFormatType.NATIVE_ARROW) { + // Only verify getString for Arrow since JSON representations have difficulties with + // floating point toString conversion (3.300000000000000e+00 vs 3.3 in native arrow) + String expectedArrowGetStringResult = + "{\"string\": \"a\",\"b\": 1,\"s\": 2,\"i\": 3,\"l\": 4,\"f\": 1.1,\"d\": 2.2,\"bd\": 3.3,\"bool\": true,\"timestamp_ltz\": \"Wed, 22 Dec 2021 09:43:44 +0100\",\"timestamp_ntz\": \"Thu, 23 Dec 2021 09:44:44 Z\",\"timestamp_tz\": \"Fri, 24 Dec 2021 09:45:45 +0800\",\"date\": \"2023-12-24\",\"time\": \"12:34:56\",\"binary\": \"616263\",\"simpleClass\": {\"string\": \"b\",\"intValue\": 2}}"; + assertEquals(expectedArrowGetStringResult, resultSet.getString(1)); + } } } } @@ -234,43 +207,7 @@ public void testReturnStructAsStringIfTypeWasNotIndicated() throws SQLException + "TIMESTAMP_LTZ_OUTPUT_FORMAT='YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM'," + "TIMESTAMP_NTZ_OUTPUT_FORMAT='YYYY-MM-DD HH24:MI:SS.FF3'"); - try (ResultSet resultSet = - statement.executeQuery( - "select {" - + "'string': 'a', " - + "'b': 1, " - + "'s': 2, " - + "'i': 3, " - + "'l': 4, " - + "'f': 1.1, " - + "'d': 2.2, " - + "'bd': 3.3, " - + "'bool': true, " - + "'timestamp_ltz': '2021-12-22 09:43:44'::TIMESTAMP_LTZ, " - + "'timestamp_ntz': '2021-12-23 09:44:44'::TIMESTAMP_NTZ, " - + "'timestamp_tz': '2021-12-24 09:45:45 +0800'::TIMESTAMP_TZ, " - + "'date': '2023-12-24'::DATE, " - + "'time': '12:34:56'::TIME, " - + "'binary': TO_BINARY('616263', 'HEX'), " - + "'simpleClass': {'string': 'b', 'intValue': 2}" - + "}::OBJECT(" - + "string VARCHAR, " - + "b TINYINT, " - + "s SMALLINT, " - + "i INTEGER, " - + "l BIGINT, " - + "f FLOAT, " - + "d DOUBLE, " - + "bd DOUBLE, " - + "bool BOOLEAN, " - + "timestamp_ltz TIMESTAMP_LTZ, " - + "timestamp_ntz TIMESTAMP_NTZ, " - + "timestamp_tz TIMESTAMP_TZ, " - + "date DATE, " - + "time TIME, " - + "binary BINARY, " - + "simpleClass OBJECT(string VARCHAR, intValue INTEGER)" - + ")"); ) { + try (ResultSet resultSet = statement.executeQuery(AllTypesClass.ALL_TYPES_QUERY); ) { resultSet.next(); String object = (String) resultSet.getObject(1); String expected = @@ -849,7 +786,7 @@ public void testMapArrayOfArrays() throws SQLException { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testMapNestedStructures() throws SQLException { - withFirstRow( + String structSelectStatement = "SELECT {'simpleClass': {'string': 'a', 'intValue': 2}, " + "'simpleClasses': ARRAY_CONSTRUCT({'string': 'a', 'intValue': 2}, {'string': 'b', 'intValue': 2}), " + "'arrayOfSimpleClasses': ARRAY_CONSTRUCT({'string': 'a', 'intValue': 2}, {'string': 'b', 'intValue': 2}), " @@ -863,7 +800,11 @@ public void testMapNestedStructures() throws SQLException { + "mapOfSimpleClasses MAP(VARCHAR, OBJECT(string VARCHAR, intValue INTEGER))," + "texts ARRAY(VARCHAR)," + "arrayOfDates ARRAY(DATE)," - + "mapOfIntegers MAP(VARCHAR, INTEGER))", + + "mapOfIntegers MAP(VARCHAR, INTEGER))"; + String expectedQueryResult = + "{\"simpleClass\": {\"string\": \"a\",\"intValue\": 2},\"simpleClasses\": [{\"string\": \"a\",\"intValue\": 2},{\"string\": \"b\",\"intValue\": 2}],\"arrayOfSimpleClasses\": [{\"string\": \"a\",\"intValue\": 2},{\"string\": \"b\",\"intValue\": 2}],\"mapOfSimpleClasses\": {\"x\": {\"string\": \"c\",\"intValue\": 2},\"y\": {\"string\": \"d\",\"intValue\": 2}},\"texts\": [\"string\",\"a\"],\"arrayOfDates\": [\"2023-12-24\",\"2023-12-25\"],\"mapOfIntegers\": {\"x\": 3,\"y\": 4}}"; + withFirstRow( + structSelectStatement, (resultSet) -> { NestedStructSqlData nestedStructSqlData = resultSet.getObject(1, NestedStructSqlData.class); @@ -908,6 +849,7 @@ public void testMapNestedStructures() throws SQLException { assertEquals(Integer.valueOf(3), nestedStructSqlData.getMapOfIntegers().get("x")); assertEquals(Integer.valueOf(4), nestedStructSqlData.getMapOfIntegers().get("y")); + TestUtil.assertEqualsIgnoringWhitespace(expectedQueryResult, resultSet.getString(1)); }); } diff --git a/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java new file mode 100644 index 000000000..a4bdb1194 --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java @@ -0,0 +1,135 @@ +package net.snowflake.client.jdbc.structuredtypes; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import net.snowflake.client.ConditionalIgnoreRule; +import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryResultSet; +import net.snowflake.client.jdbc.ResultSetFormatType; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +@Category(TestCategoryResultSet.class) +public class StructuredTypesGetStringArrowJsonCompatibilityIT + extends StructuredTypesGetStringBaseIT { + + private final String expectedStructureTypeRepresentation; + private final String selectSql; + private static Map connections = new HashMap<>(); + + public StructuredTypesGetStringArrowJsonCompatibilityIT( + ResultSetFormatType queryResultFormat, + String selectSql, + String expectedStructureTypeRepresentation) { + super(queryResultFormat); + this.selectSql = selectSql; + this.expectedStructureTypeRepresentation = expectedStructureTypeRepresentation; + } + + @Before + public void setUpConnection() throws SQLException { + // We initialize connection here since we need to set server properties that cannot be set in GH + // actions and before class is running even when all the tests have conditional ignore of tests + Connection connection = connections.get(queryResultFormat); + if (connection == null) { + connections.put(queryResultFormat, initConnection(queryResultFormat)); + } + } + + @AfterClass + public static void closeConnections() throws SQLException { + for (Connection connection : connections.values()) { + connection.close(); + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testRunAsGetString() throws SQLException { + withFirstRow( + connections.get(queryResultFormat), + selectSql, + (resultSet) -> assertGetStringIsCompatible(resultSet, expectedStructureTypeRepresentation)); + } + + @Parameterized.Parameters(name = "format={0},sql={1}") + public static Collection data() { + Map samples = new LinkedHashMap<>(); + samples.put("select {'a':3}::map(text, int);", "{\"a\":3}"); + samples.put( + "select {'a':'zażółć gęślą jaźń'}::map(text, text);", "{\"a\":\"zażółć gęślą jaźń\"}"); + samples.put("select {'a':'bla'}::map(text, text);", "{\"a\":\"bla\"}"); + samples.put("select {'1':'bla'}::map(int, text);", "{\"1\":\"bla\"}"); + samples.put("select {'1':[1,2,3]}::map(int, ARRAY(int));", "{\"1\":[1,2,3]}"); + samples.put( + "select {'1':{'string':'a'}}::map(int, OBJECT(string VARCHAR));", + "{\"1\":{\"string\":\"a\"}}"); + samples.put( + "select {'1':{'string':'a'}}::map(int, map(string, string));", + "{\"1\":{\"string\":\"a\"}}"); + samples.put( + "select {'1':[{'string':'a'},{'bla':'ble'}]}::map(int, array(map(string, string)));", + "{\"1\":[{\"string\":\"a\"},{\"bla\":\"ble\"}]}"); + samples.put("select [1,2,3]::array(int)", "[1,2,3]"); + samples.put( + "select [{'a':'a'}, {'b':'b'}]::array(map(string, string))", + "[{\"a\":\"a\"}, {\"b\":\"b\"}]"); + samples.put( + "select [{'string':'a'}, {'string':'b'}]::array(object(string varchar))", + "[{\"string\":\"a\"}, {\"string\":\"b\"}]"); + samples.put("select {'string':'a'}::object(string varchar)", "{\"string\":\"a\"}"); + samples.put( + "select {'x':'a','b':'a','c':'a','d':'a','e':'a'}::object(x varchar,b varchar,c varchar,d varchar,e varchar)", + "{\"x\":\"a\",\"b\":\"a\",\"c\":\"a\",\"d\":\"a\",\"e\":\"a\"}"); + samples.put("select {'string':[1,2,3]}::object(string array(int))", "{\"string\":[1,2,3]}"); + samples.put( + "select {'string':{'a':15}}::object(string object(a int))", "{\"string\":{\"a\":15}}"); + samples.put( + "select {'string':{'a':15}}::object(string map(string,int))", "{\"string\":{\"a\":15}}"); + samples.put( + "select {'string':{'a':{'b':15}}}::object(string object(a map(string, int)))", + "{\"string\":{\"a\":{\"b\":15}}}"); + + samples.put( + "select {'string':{'a':{'b':[{'c': 15}]}}}::object(string map(string, object(b array(object(c int)))))", + "{\"string\":{\"a\":{\"b\":[{\"c\":15}]}}}"); + // DY, DD MON YYYY HH24:MI:SS TZHTZM + samples.put( + "select {'ltz': '2024-05-20 11:22:33'::TIMESTAMP_LTZ}::object(ltz TIMESTAMP_LTZ)", + "{\"ltz\":\"Mon, 20 May 2024 11:22:33 +0200\"}"); + samples.put( + "select {'ntz': '2024-05-20 11:22:33'::TIMESTAMP_NTZ}::object(ntz TIMESTAMP_NTZ)", + "{\"ntz\":\"Mon, 20 May 2024 11:22:33 Z\"}"); + samples.put( + "select {'tz': '2024-05-20 11:22:33+0800'::TIMESTAMP_TZ}::object(tz TIMESTAMP_TZ)", + "{\"tz\":\"Mon, 20 May 2024 11:22:33 +0800\"}"); + samples.put( + "select {'date': '2024-05-20'::DATE}::object(date DATE)", "{\"date\":\"2024-05-20\"}"); + samples.put("select {'time': '22:14:55'::TIME}::object(time TIME)", "{\"time\":\"22:14:55\"}"); + samples.put("select {'bool': TRUE}::object(bool BOOLEAN)", "{\"bool\":true}"); + samples.put("select {'bool': 'y'}::object(bool BOOLEAN)", "{\"bool\":true}"); + samples.put( + "select {'binary': TO_BINARY('616263', 'HEX')}::object(binary BINARY)", + "{\"binary\":\"616263\"}"); + samples.put("select [1,2,3]::VECTOR(INT, 3)", "[1,2,3]"); + samples.put("select ['a','b','c']::ARRAY(varchar)", "[\"a\",\"b\",\"c\"]"); + + Collection parameters = new ArrayList<>(); + for (ResultSetFormatType resultSetFormatType : ResultSetFormatType.values()) { + samples.forEach( + (sql, expected) -> parameters.add(new Object[] {resultSetFormatType, sql, expected})); + } + + return parameters; + } +} diff --git a/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringBaseIT.java b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringBaseIT.java new file mode 100644 index 000000000..d9d5c15e2 --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringBaseIT.java @@ -0,0 +1,68 @@ +package net.snowflake.client.jdbc.structuredtypes; + +import static org.junit.Assert.assertTrue; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import net.snowflake.client.TestUtil; +import net.snowflake.client.ThrowingConsumer; +import net.snowflake.client.jdbc.BaseJDBCTest; +import net.snowflake.client.jdbc.ResultSetFormatType; + +abstract class StructuredTypesGetStringBaseIT extends BaseJDBCTest { + + protected final ResultSetFormatType queryResultFormat; + + public StructuredTypesGetStringBaseIT(ResultSetFormatType queryResultFormat) { + this.queryResultFormat = queryResultFormat; + } + + protected Connection init() throws SQLException { + return initConnection(this.queryResultFormat); + } + + protected static Connection initConnection(ResultSetFormatType queryResultFormat) + throws SQLException { + Connection conn = BaseJDBCTest.getConnection(BaseJDBCTest.DONT_INJECT_SOCKET_TIMEOUT); + try (Statement stmt = conn.createStatement()) { + stmt.execute("alter session set USE_CACHED_RESULT = false"); + stmt.execute("alter session set ENABLE_STRUCTURED_TYPES_IN_CLIENT_RESPONSE = true"); + stmt.execute("alter session set IGNORE_CLIENT_VESRION_IN_STRUCTURED_TYPES_RESPONSE = true"); + stmt.execute("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'"); + stmt.execute( + "alter session set " + + "TIMESTAMP_TYPE_MAPPING='TIMESTAMP_LTZ'," + + "TIMESTAMP_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'," + + "TIMESTAMP_TZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'," + + "TIMESTAMP_LTZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'," + + "TIMESTAMP_NTZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'"); + stmt.execute( + "alter session set jdbc_query_result_format = '" + + queryResultFormat.sessionParameterTypeValue + + "'"); + if (queryResultFormat == ResultSetFormatType.NATIVE_ARROW) { + stmt.execute("alter session set ENABLE_STRUCTURED_TYPES_NATIVE_ARROW_FORMAT = true"); + stmt.execute("alter session set FORCE_ENABLE_STRUCTURED_TYPES_NATIVE_ARROW_FORMAT = true"); + } + } + return conn; + } + + protected void assertGetStringIsCompatible(ResultSet resultSet, String expected) + throws SQLException { + String result = resultSet.getString(1); + TestUtil.assertEqualsIgnoringWhitespace(expected, result); + } + + protected void withFirstRow( + Connection connection, String sqlText, ThrowingConsumer consumer) + throws SQLException { + try (Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(sqlText); ) { + assertTrue(rs.next()); + consumer.accept(rs); + } + } +} diff --git a/src/test/java/net/snowflake/client/jdbc/structuredtypes/sqldata/AllTypesClass.java b/src/test/java/net/snowflake/client/jdbc/structuredtypes/sqldata/AllTypesClass.java index 3f12a9f63..f8b494a87 100644 --- a/src/test/java/net/snowflake/client/jdbc/structuredtypes/sqldata/AllTypesClass.java +++ b/src/test/java/net/snowflake/client/jdbc/structuredtypes/sqldata/AllTypesClass.java @@ -11,6 +11,43 @@ import net.snowflake.client.jdbc.SnowflakeColumn; public class AllTypesClass implements SQLData { + public static String ALL_TYPES_QUERY = + "select {" + + "'string': 'a', " + + "'b': 1, " + + "'s': 2, " + + "'i': 3, " + + "'l': 4, " + + "'f': 1.1, " + + "'d': 2.2, " + + "'bd': 3.3, " + + "'bool': true, " + + "'timestamp_ltz': '2021-12-22 09:43:44'::TIMESTAMP_LTZ, " + + "'timestamp_ntz': '2021-12-23 09:44:44'::TIMESTAMP_NTZ, " + + "'timestamp_tz': '2021-12-24 09:45:45 +0800'::TIMESTAMP_TZ, " + + "'date': '2023-12-24'::DATE, " + + "'time': '12:34:56'::TIME, " + + "'binary': TO_BINARY('616263', 'HEX'), " + + "'simpleClass': {'string': 'b', 'intValue': 2}" + + "}::OBJECT(" + + "string VARCHAR, " + + "b TINYINT, " + + "s SMALLINT, " + + "i INTEGER, " + + "l BIGINT, " + + "f FLOAT, " + + "d DOUBLE, " + + "bd DOUBLE, " + + "bool BOOLEAN, " + + "timestamp_ltz TIMESTAMP_LTZ, " + + "timestamp_ntz TIMESTAMP_NTZ, " + + "timestamp_tz TIMESTAMP_TZ, " + + "date DATE, " + + "time TIME, " + + "binary BINARY, " + + "simpleClass OBJECT(string VARCHAR, intValue INTEGER)" + + ")"; + private String string; private Byte b; private Short s; From bbc848c1217f6ae71c7dcfde0feb46d481013684 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Mon, 30 Sep 2024 08:34:34 +0200 Subject: [PATCH 05/53] =?UTF-8?q?Revert=20"SNOW-1501662=20Allow=20JDBC=20t?= =?UTF-8?q?o=20handle=20ZSTD=20compressed=20http=20respon=E2=80=A6=20(#190?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FIPS/pom.xml | 4 - FIPS/scripts/check_content.sh | 4 +- ci/scripts/check_content.sh | 6 +- linkage-checker-exclusion-rules.xml | 5 + parent-pom.xml | 10 -- pom.xml | 4 - .../jdbc/DefaultResultStreamProvider.java | 3 - .../jdbc/DefaultResultStreamProviderTest.java | 120 ------------------ thin_public_pom.xml | 6 - 9 files changed, 10 insertions(+), 152 deletions(-) delete mode 100644 src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 9803c4db7..654792858 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -429,10 +429,6 @@ org.jsoup ${shadeBase}.org.jsoup - - com.github.luben.zstd - ${shadeBase}.com.github.luben.zstd - com.nimbusds ${shadeBase}.com.nimbusds diff --git a/FIPS/scripts/check_content.sh b/FIPS/scripts/check_content.sh index fa675655e..8b818b1b4 100755 --- a/FIPS/scripts/check_content.sh +++ b/FIPS/scripts/check_content.sh @@ -1,12 +1,12 @@ #!/bin/bash -e -# scripts used to check if all dependencies are shaded into snowflake internal path +# scripts used to check if all dependency is shaded into snowflake internal path set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then +if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi diff --git a/ci/scripts/check_content.sh b/ci/scripts/check_content.sh index ce6f34180..a9c0768b6 100755 --- a/ci/scripts/check_content.sh +++ b/ci/scripts/check_content.sh @@ -8,12 +8,12 @@ set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then - echo "[ERROR] JDBC jar includes multi-release classes not under the snowflake namespace" +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake"; then + echo "[ERROR] JDBC jar includes multi release classes not under the snowflake namespace" exit 1 fi diff --git a/linkage-checker-exclusion-rules.xml b/linkage-checker-exclusion-rules.xml index 0f6275008..8bad89714 100644 --- a/linkage-checker-exclusion-rules.xml +++ b/linkage-checker-exclusion-rules.xml @@ -19,6 +19,11 @@ Optional + + + + Optional + diff --git a/parent-pom.xml b/parent-pom.xml index ab6948da5..8adb90553 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -19,7 +19,6 @@ 1.10.0 4.5.14 4.4.16 - 1.5.6-5 17.0.0 9.3 1.8.1 @@ -328,11 +327,6 @@ httpcore ${apache.httpcore.version} - - com.github.luben - zstd-jni - ${zstd-jni.version} - org.apache.tika tika-core @@ -650,10 +644,6 @@ org.apache.httpcomponents httpcore - - com.github.luben - zstd-jni - org.apache.tika tika-core diff --git a/pom.xml b/pom.xml index bf917e1a4..80428f55a 100644 --- a/pom.xml +++ b/pom.xml @@ -947,10 +947,6 @@ android.annotation ${shadeBase}.android.annotation - - com.github.luben.zstd - ${shadeBase}.com.github.luben.zstd - diff --git a/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java b/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java index 567db8fa1..3ee556bb4 100644 --- a/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java +++ b/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java @@ -2,7 +2,6 @@ import static net.snowflake.client.core.Constants.MB; -import com.github.luben.zstd.ZstdInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; @@ -154,8 +153,6 @@ private InputStream detectContentEncodingAndGetInputStream(HttpResponse response if ("gzip".equalsIgnoreCase(encoding.getValue())) { /* specify buffer size for GZIPInputStream */ inputStream = new GZIPInputStream(is, STREAM_BUFFER_SIZE); - } else if ("zstd".equalsIgnoreCase(encoding.getValue())) { - inputStream = new ZstdInputStream(is); } else { throw new SnowflakeSQLException( SqlState.INTERNAL_ERROR, diff --git a/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java b/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java deleted file mode 100644 index b78bf3a5e..000000000 --- a/src/test/java/net/snowflake/client/jdbc/DefaultResultStreamProviderTest.java +++ /dev/null @@ -1,120 +0,0 @@ -package net.snowflake.client.jdbc; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.github.luben.zstd.ZstdInputStream; -import com.github.luben.zstd.ZstdOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.junit.Before; -import org.junit.Test; - -public class DefaultResultStreamProviderTest { - - private DefaultResultStreamProvider resultStreamProvider; - private HttpResponse mockResponse; - - @Before - public void setUp() { - resultStreamProvider = new DefaultResultStreamProvider(); - mockResponse = mock(HttpResponse.class); - } - - private InputStream invokeDetectContentEncodingAndGetInputStream( - HttpResponse response, InputStream inputStream) throws Exception { - Method method = - DefaultResultStreamProvider.class.getDeclaredMethod( - "detectContentEncodingAndGetInputStream", HttpResponse.class, InputStream.class); - method.setAccessible(true); - return (InputStream) method.invoke(resultStreamProvider, response, inputStream); - } - - @Test - public void testDetectContentEncodingAndGetInputStream_Gzip() throws Exception { - // Mocking gzip content encoding - Header encodingHeader = mock(Header.class); - when(encodingHeader.getValue()).thenReturn("gzip"); - when(mockResponse.getFirstHeader("Content-Encoding")).thenReturn(encodingHeader); - - // Original data to compress and validate - String originalData = "Some data in GZIP"; - - // Creating a gzip byte array using GZIPOutputStream - byte[] gzipData; - try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) { - gzipOutputStream.write(originalData.getBytes(StandardCharsets.UTF_8)); - gzipOutputStream.close(); // close to flush and finish the compression - gzipData = byteArrayOutputStream.toByteArray(); - } - - // Mocking input stream with the gzip data - InputStream gzipStream = new ByteArrayInputStream(gzipData); - - // Call the private method using reflection - InputStream resultStream = - invokeDetectContentEncodingAndGetInputStream(mockResponse, gzipStream); - - // Decompress and validate the data matches original - ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int bytesRead; - try (GZIPInputStream gzipInputStream = (GZIPInputStream) resultStream) { - while ((bytesRead = gzipInputStream.read(buffer)) != -1) { - decompressedOutput.write(buffer, 0, bytesRead); - } - } - String decompressedData = new String(decompressedOutput.toByteArray(), StandardCharsets.UTF_8); - - assertEquals(originalData, decompressedData); - } - - @Test - public void testDetectContentEncodingAndGetInputStream_Zstd() throws Exception { - // Mocking zstd content encoding - Header encodingHeader = mock(Header.class); - when(encodingHeader.getValue()).thenReturn("zstd"); - when(mockResponse.getFirstHeader("Content-Encoding")).thenReturn(encodingHeader); - - // Original data to compress and validate - String originalData = "Some data in ZSTD"; - - // Creating a zstd byte array using ZstdOutputStream - byte[] zstdData; - try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - ZstdOutputStream zstdOutputStream = new ZstdOutputStream(byteArrayOutputStream)) { - zstdOutputStream.write(originalData.getBytes(StandardCharsets.UTF_8)); - zstdOutputStream.close(); // close to flush and finish the compression - zstdData = byteArrayOutputStream.toByteArray(); - } - - // Mocking input stream with the zstd data - InputStream zstdStream = new ByteArrayInputStream(zstdData); - - // Call the private method using reflection - InputStream resultStream = - invokeDetectContentEncodingAndGetInputStream(mockResponse, zstdStream); - - // Decompress and validate the data matches original - ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int bytesRead; - try (ZstdInputStream zstdInputStream = (ZstdInputStream) resultStream) { - while ((bytesRead = zstdInputStream.read(buffer)) != -1) { - decompressedOutput.write(buffer, 0, bytesRead); - } - } - String decompressedData = new String(decompressedOutput.toByteArray(), StandardCharsets.UTF_8); - - assertEquals(originalData, decompressedData); - } -} diff --git a/thin_public_pom.xml b/thin_public_pom.xml index a781a376d..31a1aedee 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -64,7 +64,6 @@ UTF-8 2.0.13 1.6.9 - 1.5.6-5 @@ -263,11 +262,6 @@ jsoup ${jsoup.version} - - com.github.luben - zstd-jni - ${zstd-jni.version} - org.slf4j slf4j-api From 06ca7527fb2e72d42e3e0bea3f7e1bf1a31b0e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kubik?= Date: Tue, 1 Oct 2024 08:56:05 +0200 Subject: [PATCH 06/53] SNOW-1700248 Revert turning native arrow boolean primitives lowercase (#1906) Turn primitive types back to lowercase to prevent SFTest tests from failing --- .../java/net/snowflake/client/core/ResultUtil.java | 2 +- .../client/core/arrow/BitToBooleanConverter.java | 2 +- .../ArrowStringRepresentationBuilderBase.java | 12 ++++++++++++ .../client/core/arrow/BitToBooleanConverterTest.java | 2 +- .../client/core/json/StringConverterTest.java | 8 ++++---- .../client/jdbc/ResultSetJsonVsArrowIT.java | 4 ++-- .../net/snowflake/client/jdbc/ResultSetLatestIT.java | 2 +- ...cturedTypesGetStringArrowJsonCompatibilityIT.java | 3 +++ 8 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/ResultUtil.java b/src/main/java/net/snowflake/client/core/ResultUtil.java index 6c00f513e..b894f4259 100644 --- a/src/main/java/net/snowflake/client/core/ResultUtil.java +++ b/src/main/java/net/snowflake/client/core/ResultUtil.java @@ -251,7 +251,7 @@ public static String getSFTimeAsString( * @return boolean in string */ public static String getBooleanAsString(boolean bool) { - return bool ? "true" : "false"; + return bool ? "TRUE" : "FALSE"; } /** diff --git a/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java b/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java index 640ff68ca..2f5a8cf83 100644 --- a/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java @@ -58,7 +58,7 @@ public Object toObject(int index) { @Override public String toString(int index) { - return isNull(index) ? null : toBoolean(index) ? "true" : "false"; + return isNull(index) ? null : toBoolean(index) ? "TRUE" : "FALSE"; } @Override diff --git a/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowStringRepresentationBuilderBase.java b/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowStringRepresentationBuilderBase.java index 5b83f4497..cc25bb7e0 100644 --- a/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowStringRepresentationBuilderBase.java +++ b/src/main/java/net/snowflake/client/core/arrow/tostringhelpers/ArrowStringRepresentationBuilderBase.java @@ -6,6 +6,11 @@ import net.snowflake.client.core.SnowflakeJdbcInternalApi; import net.snowflake.client.jdbc.SnowflakeType; +/** + * StringBuilder like class to aggregate the string representation of snowflake Native ARROW + * structured types as JSON one-liners. Provides some additional snowflake-specific logic in order + * to determine whether the value should be quoted or case should be changed. + */ @SnowflakeJdbcInternalApi public abstract class ArrowStringRepresentationBuilderBase { private final StringJoiner joiner; @@ -39,6 +44,13 @@ private boolean shouldQuoteValue(SnowflakeType type) { } protected String quoteIfNeeded(String string, SnowflakeType type) { + // Turn Boolean string representations lowercase to make the output JSON-compatible + // this should be changed on the converter level, but it would be a breaking change thus + // for now only structured types will be valid JSONs while in NATIVE ARROW mode + if (type == SnowflakeType.BOOLEAN) { + string = string.toLowerCase(); + } + if (shouldQuoteValue(type)) { return '"' + string + '"'; } diff --git a/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java index 1fd65d911..e5091d6fc 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java @@ -73,7 +73,7 @@ public void testConvertToString() throws SFException { } else { assertThat(boolVal, is(expectedValues.get(i))); assertThat(objectVal, is(expectedValues.get(i))); - assertThat(stringVal, is(expectedValues.get(i).toString())); + assertThat(stringVal, is(expectedValues.get(i).toString().toUpperCase())); if (boolVal) { assertThat((byte) 0x1, is(converter.toBytes(i)[0])); } else { diff --git a/src/test/java/net/snowflake/client/core/json/StringConverterTest.java b/src/test/java/net/snowflake/client/core/json/StringConverterTest.java index 748548966..5fe3dd2cb 100644 --- a/src/test/java/net/snowflake/client/core/json/StringConverterTest.java +++ b/src/test/java/net/snowflake/client/core/json/StringConverterTest.java @@ -59,10 +59,10 @@ public void testConvertingString() throws SFException { @Test public void testConvertingBoolean() throws SFException { - assertEquals("true", stringConverter.getString(true, Types.BOOLEAN, Types.BOOLEAN, 0)); - assertEquals("true", stringConverter.getString("true", Types.BOOLEAN, Types.BOOLEAN, 0)); - assertEquals("false", stringConverter.getString(false, Types.BOOLEAN, Types.BOOLEAN, 0)); - assertEquals("false", stringConverter.getString("false", Types.BOOLEAN, Types.BOOLEAN, 0)); + assertEquals("TRUE", stringConverter.getString(true, Types.BOOLEAN, Types.BOOLEAN, 0)); + assertEquals("TRUE", stringConverter.getString("true", Types.BOOLEAN, Types.BOOLEAN, 0)); + assertEquals("FALSE", stringConverter.getString(false, Types.BOOLEAN, Types.BOOLEAN, 0)); + assertEquals("FALSE", stringConverter.getString("false", Types.BOOLEAN, Types.BOOLEAN, 0)); } @Test diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java index 0dd8edd47..65cc27242 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java @@ -1501,12 +1501,12 @@ public void testBoolean() throws SQLException { ResultSet rs = statement.executeQuery("select * from " + table)) { assertTrue(rs.next()); assertTrue(rs.getBoolean(1)); - assertEquals("true", rs.getString(1)); + assertEquals("TRUE", rs.getString(1)); assertTrue(rs.next()); assertFalse(rs.getBoolean(1)); assertTrue(rs.next()); assertFalse(rs.getBoolean(1)); - assertEquals("false", rs.getString(1)); + assertEquals("FALSE", rs.getString(1)); assertFalse(rs.next()); statement.execute("drop table if exists " + table); } diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java index 3ad105cea..dc16d5dcf 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java @@ -1176,7 +1176,7 @@ public void testGetObjectWithType() throws SQLException { assertResultValueAndType(statement, Double.valueOf("1.1"), "f", Double.class); assertResultValueAndType(statement, Double.valueOf("2.2"), "d", Double.class); assertResultValueAndType(statement, BigDecimal.valueOf(3.3), "bd", BigDecimal.class); - assertResultValueAndType(statement, "false", "bool", String.class); + assertResultValueAndType(statement, "FALSE", "bool", String.class); assertResultValueAndType(statement, Boolean.FALSE, "bool", Boolean.class); assertResultValueAndType(statement, 0L, "bool", Long.class); assertResultValueAsString( diff --git a/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java index a4bdb1194..352d2b1a4 100644 --- a/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java +++ b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java @@ -84,6 +84,9 @@ public static Collection data() { samples.put( "select [{'a':'a'}, {'b':'b'}]::array(map(string, string))", "[{\"a\":\"a\"}, {\"b\":\"b\"}]"); + samples.put( + "select [{'a':true}, {'b':false}]::array(map(string, boolean))", + "[{\"a\":true}, {\"b\":false}]"); samples.put( "select [{'string':'a'}, {'string':'b'}]::array(object(string varchar))", "[{\"string\":\"a\"}, {\"string\":\"b\"}]"); From a5b4cdb54c11a1842962d1c507ccdeda0850ca57 Mon Sep 17 00:00:00 2001 From: Waleed Fateem <72769898+sfc-gh-wfateem@users.noreply.github.com> Date: Tue, 1 Oct 2024 06:31:25 -0500 Subject: [PATCH 07/53] SNOW-1675321 Remove Account Identifier Question From Templates (#1898) --- .github/ISSUE_TEMPLATE/BUG_REPORT.md | 3 ++- .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md index 2c4c5bc91..dcbb8109f 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.md +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -37,4 +37,5 @@ In order to accurately debug the issue this information is required. Thanks! https://community.snowflake.com/s/article/How-to-generate-log-file-on-Snowflake-connectors -7. What is your Snowflake account identifier, if any? (Optional) + Before sharing any information, please be sure to review the log and remove any sensitive + information. diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md index 83c2ada99..a3b4e6517 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -17,6 +17,4 @@ otherwise continue here. ## How would this improve `snowflake-jdbc`? ## References, Other Background - -## What is your Snowflake account identifier, if any? From 5e4585b7d191e3cd316d280e2cc4c91c445ae54a Mon Sep 17 00:00:00 2001 From: Kaushil Shah Date: Tue, 1 Oct 2024 10:07:24 -0700 Subject: [PATCH 08/53] SNOW-1528939 - Updated 3rd party maven proxy reference from nexus to artifactory (#1905) --- FIPS/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 654792858..b747a02ce 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -21,7 +21,7 @@ Central Internal Central Repo2 default - https://nexus.int.snowflakecomputing.com/repository/maven-central/ + https://artifactory.ci1.us-west-2.aws-dev.app.snowflake.com/artifactory/development-maven-virtual/ false From 1c36229b2556b56b9dc2f673f37edb843d9a4420 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 2 Oct 2024 16:06:33 +0200 Subject: [PATCH 09/53] Bump version to 3.19.1 for release (#1907) Co-authored-by: Jenkins User <900904> --- CHANGELOG.rst | 4 ++++ FIPS/pom.xml | 4 ++-- parent-pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b83a77291..72c788c36 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,7 @@ +**JDBC Driver 3.19.1** + +- \||Please Refer to Release Notes at https://docs.snowflake.com/en/release-notes/clients-drivers/jdbc + **JDBC Driver 3.19.0** - \||Please Refer to Release Notes at https://docs.snowflake.com/en/release-notes/clients-drivers/jdbc diff --git a/FIPS/pom.xml b/FIPS/pom.xml index b747a02ce..503f75cbc 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -5,12 +5,12 @@ net.snowflake snowflake-jdbc-parent - 3.19.1-SNAPSHOT + 3.19.1 ../parent-pom.xml snowflake-jdbc-fips - 3.19.1-SNAPSHOT + 3.19.1 jar snowflake-jdbc-fips diff --git a/parent-pom.xml b/parent-pom.xml index 8adb90553..002c5c621 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -5,7 +5,7 @@ net.snowflake snowflake-jdbc-parent - 3.19.1-SNAPSHOT + 3.19.1 pom diff --git a/pom.xml b/pom.xml index 80428f55a..4bc83e067 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ net.snowflake snowflake-jdbc-parent - 3.19.1-SNAPSHOT + 3.19.1 ./parent-pom.xml ${artifactId} - 3.19.1-SNAPSHOT + 3.19.1 jar ${artifactId} From d26df3a367fbbde9082372e1d6f4a1c168ed31b9 Mon Sep 17 00:00:00 2001 From: David Szmolka <69192509+sfc-gh-dszmolka@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:05:49 +0200 Subject: [PATCH 10/53] SNOW-1726146 bump protobuf-java to 3.25.5 (#1910) --- parent-pom.xml | 2 +- thin_public_pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parent-pom.xml b/parent-pom.xml index 002c5c621..3027076ac 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -49,7 +49,7 @@ 32.1.1-jre 1.43.3 3.0.2 - 3.23.3 + 3.25.5 1.60.0 2.2 2.4.3 diff --git a/thin_public_pom.xml b/thin_public_pom.xml index 31a1aedee..8069afd9d 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -49,7 +49,7 @@ 32.1.1-jre 1.43.3 3.0.2 - 3.23.3 + 3.25.5 1.60.0 2.17.2 3.1.0 From 1183b9dbc3dd6f849b0562a4d2de82ba0ab11f61 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 9 Oct 2024 08:24:29 +0200 Subject: [PATCH 11/53] Revert "Bump version to 3.19.1 for release (#1907)" (#1911) --- CHANGELOG.rst | 4 ---- FIPS/pom.xml | 4 ++-- parent-pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 72c788c36..b83a77291 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,3 @@ -**JDBC Driver 3.19.1** - -- \||Please Refer to Release Notes at https://docs.snowflake.com/en/release-notes/clients-drivers/jdbc - **JDBC Driver 3.19.0** - \||Please Refer to Release Notes at https://docs.snowflake.com/en/release-notes/clients-drivers/jdbc diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 503f75cbc..b747a02ce 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -5,12 +5,12 @@ net.snowflake snowflake-jdbc-parent - 3.19.1 + 3.19.1-SNAPSHOT ../parent-pom.xml snowflake-jdbc-fips - 3.19.1 + 3.19.1-SNAPSHOT jar snowflake-jdbc-fips diff --git a/parent-pom.xml b/parent-pom.xml index 3027076ac..cb1b52b8e 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -5,7 +5,7 @@ net.snowflake snowflake-jdbc-parent - 3.19.1 + 3.19.1-SNAPSHOT pom diff --git a/pom.xml b/pom.xml index 4bc83e067..80428f55a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ net.snowflake snowflake-jdbc-parent - 3.19.1 + 3.19.1-SNAPSHOT ./parent-pom.xml ${artifactId} - 3.19.1 + 3.19.1-SNAPSHOT jar ${artifactId} From 51328b7043d2246b1b5c55e4a9c887d04af8ebe3 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:07:21 +0200 Subject: [PATCH 12/53] SNOW-1730937: Remove repopositories definition from FIPS pom (#1912) --- FIPS/pom.xml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index b747a02ce..48e57b1ad 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -16,18 +16,6 @@ snowflake-jdbc-fips http://maven.apache.org - - - Central - Internal Central Repo2 - default - https://artifactory.ci1.us-west-2.aws-dev.app.snowflake.com/artifactory/development-maven-virtual/ - - false - - - - 3.3.9 From 0466d0ec9de55662d3c12d3fbd3360b384b39c01 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:04:12 +0200 Subject: [PATCH 13/53] SNOW-1732777: Log cancel query reason (#1914) --- .../client/core/CancellationReason.java | 11 +++++++++++ .../snowflake/client/core/SFBaseStatement.java | 14 ++++++++++++++ .../net/snowflake/client/core/SFStatement.java | 15 +++++++++++---- .../net/snowflake/client/core/StmtUtil.java | 17 ++++++++++++++++- .../client/jdbc/SnowflakeStatementV1.java | 3 ++- 5 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/snowflake/client/core/CancellationReason.java diff --git a/src/main/java/net/snowflake/client/core/CancellationReason.java b/src/main/java/net/snowflake/client/core/CancellationReason.java new file mode 100644 index 000000000..e3ae4e308 --- /dev/null +++ b/src/main/java/net/snowflake/client/core/CancellationReason.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.core; + +@SnowflakeJdbcInternalApi +public enum CancellationReason { + UNKNOWN, + CLIENT_REQUESTED, + TIMEOUT +} diff --git a/src/main/java/net/snowflake/client/core/SFBaseStatement.java b/src/main/java/net/snowflake/client/core/SFBaseStatement.java index 17b2fd1b6..8a6136434 100644 --- a/src/main/java/net/snowflake/client/core/SFBaseStatement.java +++ b/src/main/java/net/snowflake/client/core/SFBaseStatement.java @@ -116,9 +116,23 @@ public abstract SFBaseResultSet asyncExecute( * * @throws SFException if the statement is already closed. * @throws SQLException if there are server-side errors from trying to abort. + * @deprecated use {@link #cancel(CancellationReason)} instead */ + @Deprecated public abstract void cancel() throws SFException, SQLException; + /** + * Aborts the statement. + * + * @param cancellationReason reason for the cancellation + * @throws SFException if the statement is already closed. + * @throws SQLException if there are server-side errors from trying to abort. + */ + @SnowflakeJdbcInternalApi + public void cancel(CancellationReason cancellationReason) throws SFException, SQLException { + cancel(); // default cancel is called to keep interface backward compatibility + } + /** * Sets a property within session properties, i.e., if the sql is using set-sf-property * diff --git a/src/main/java/net/snowflake/client/core/SFStatement.java b/src/main/java/net/snowflake/client/core/SFStatement.java index 6142b8eb9..f3a0f8a09 100644 --- a/src/main/java/net/snowflake/client/core/SFStatement.java +++ b/src/main/java/net/snowflake/client/core/SFStatement.java @@ -298,7 +298,7 @@ private TimeBombTask(SFStatement statement) { @Override public Void call() throws SQLException { try { - statement.cancel(); + statement.cancel(CancellationReason.TIMEOUT); } catch (SFException ex) { throw new SnowflakeSQLLoggedException( session, ex.getSqlState(), ex.getVendorCode(), ex, ex.getParams()); @@ -711,10 +711,11 @@ private void reauthenticate() throws SFException, SnowflakeSQLException { * * @param sql sql statement * @param mediaType media type + * @param cancellationReason reason for the cancellation * @throws SnowflakeSQLException if failed to cancel the statement * @throws SFException if statement is already closed */ - private void cancelHelper(String sql, String mediaType) + private void cancelHelper(String sql, String mediaType, CancellationReason cancellationReason) throws SnowflakeSQLException, SFException { synchronized (this) { if (isClosed) { @@ -734,7 +735,7 @@ private void cancelHelper(String sql, String mediaType) .setMaxRetries(session.getMaxHttpRetries()) .setHttpClientSettingsKey(session.getHttpClientKey()); - StmtUtil.cancel(stmtInput); + StmtUtil.cancel(stmtInput, cancellationReason); synchronized (this) { /* @@ -842,6 +843,12 @@ public void close() { @Override public void cancel() throws SFException, SQLException { logger.trace("void cancel()", false); + cancel(CancellationReason.UNKNOWN); + } + + @Override + public void cancel(CancellationReason cancellationReason) throws SFException, SQLException { + logger.trace("void cancel(CancellationReason)", false); if (canceling.get()) { logger.debug("Query is already cancelled", false); @@ -866,7 +873,7 @@ public void cancel() throws SFException, SQLException { } // cancel the query on the server side if it has been issued - cancelHelper(this.sqlText, StmtUtil.SF_MEDIA_TYPE); + cancelHelper(this.sqlText, StmtUtil.SF_MEDIA_TYPE, cancellationReason); } } diff --git a/src/main/java/net/snowflake/client/core/StmtUtil.java b/src/main/java/net/snowflake/client/core/StmtUtil.java index 96fefe5dc..3d8055d1f 100644 --- a/src/main/java/net/snowflake/client/core/StmtUtil.java +++ b/src/main/java/net/snowflake/client/core/StmtUtil.java @@ -681,8 +681,23 @@ protected static JsonNode getQueryResultJSON(String queryId, SFSession session) * @param stmtInput input statement * @throws SFException if there is an internal exception * @throws SnowflakeSQLException if failed to cancel the statement + * @deprecated use {@link #cancel(StmtInput, CancellationReason)} instead */ + @Deprecated public static void cancel(StmtInput stmtInput) throws SFException, SnowflakeSQLException { + cancel(stmtInput, CancellationReason.UNKNOWN); + } + + /** + * Cancel a statement identifiable by a request id + * + * @param stmtInput input statement + * @param cancellationReason reason for the cancellation + * @throws SFException if there is an internal exception + * @throws SnowflakeSQLException if failed to cancel the statement + */ + public static void cancel(StmtInput stmtInput, CancellationReason cancellationReason) + throws SFException, SnowflakeSQLException { HttpPost httpRequest = null; AssertUtil.assertTrue( @@ -701,7 +716,7 @@ public static void cancel(StmtInput stmtInput) throws SFException, SnowflakeSQLE try { URIBuilder uriBuilder = new URIBuilder(stmtInput.serverUrl); - + logger.warn("Cancelling query {} with reason {}", stmtInput.requestId, cancellationReason); logger.debug("Aborting query: {}", stmtInput.sql); uriBuilder.setPath(SF_PATH_ABORT_REQUEST_V1); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeStatementV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeStatementV1.java index 5016c175b..08cb3fac7 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeStatementV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeStatementV1.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import net.snowflake.client.core.CancellationReason; import net.snowflake.client.core.ExecTimeTelemetryData; import net.snowflake.client.core.ParameterBindingDTO; import net.snowflake.client.core.ResultUtil; @@ -952,7 +953,7 @@ public void cancel() throws SQLException { raiseSQLExceptionIfStatementIsClosed(); try { - sfBaseStatement.cancel(); + sfBaseStatement.cancel(CancellationReason.CLIENT_REQUESTED); } catch (SFException ex) { throw new SnowflakeSQLException(ex, ex.getSqlState(), ex.getVendorCode(), ex.getParams()); } From f42535036d618abee57574e1f5355c9ff85eb4aa Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:58:47 +0200 Subject: [PATCH 14/53] SNOW-1567156: Bump bouncy castle dependencies (#1853) --- FIPS/pom.xml | 6 +++--- FIPS/public_pom.xml | 4 ++-- linkage-checker-exclusion-rules.xml | 10 ++++++++++ parent-pom.xml | 12 +++++++++--- pom.xml | 7 ++++++- thin_public_pom.xml | 7 ++++++- 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 48e57b1ad..425d263df 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -596,15 +596,15 @@ - + - + - + diff --git a/FIPS/public_pom.xml b/FIPS/public_pom.xml index d180e4a57..00bc9738c 100644 --- a/FIPS/public_pom.xml +++ b/FIPS/public_pom.xml @@ -32,8 +32,8 @@ - 1.0.2.4 - 1.0.5 + 1.0.2.5 + 1.0.7 5.13.0 diff --git a/linkage-checker-exclusion-rules.xml b/linkage-checker-exclusion-rules.xml index 8bad89714..65affa44a 100644 --- a/linkage-checker-exclusion-rules.xml +++ b/linkage-checker-exclusion-rules.xml @@ -44,6 +44,16 @@ ? + + + + ? + + + + + ? + + org.bouncycastle + bcutil-jdk18on + ${bouncycastle.version} + org.bouncycastle diff --git a/pom.xml b/pom.xml index 80428f55a..2fd7f9216 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,10 @@ org.bouncycastle bcprov-jdk18on + + org.bouncycastle + bcutil-jdk18on + @@ -1031,6 +1035,7 @@ + @@ -1105,7 +1110,7 @@ diff --git a/thin_public_pom.xml b/thin_public_pom.xml index 8069afd9d..08ab73da6 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -37,7 +37,7 @@ 4.4.16 1.12.655 5.0.0 - 1.74 + 1.78.1 1.17.0 2.11.0 1.2 @@ -117,6 +117,11 @@ bcprov-jdk18on ${bouncycastle.version} + + org.bouncycastle + bcutil-jdk18on + ${bouncycastle.version} + com.amazonaws aws-java-sdk-core From d8e90a8557e88894907772581340fa30fa8fea44 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:44:44 +0200 Subject: [PATCH 15/53] SNOW-1616480: Remove gcs presigned url test since it is not supported (#1923) --- .../client/jdbc/SnowflakeDriverLatestIT.java | 48 ------------------- 1 file changed, 48 deletions(-) diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverLatestIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverLatestIT.java index 989f1211a..91052fd7c 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverLatestIT.java @@ -1470,54 +1470,6 @@ public void testNoSpaceLeftOnDeviceException() throws SQLException { } } - @Test - @Ignore // ignored until SNOW-1616480 is resolved - public void testUploadWithGCSPresignedUrlWithoutConnection() throws Throwable { - File destFolder = tmpFolder.newFolder(); - String destFolderCanonicalPath = destFolder.getCanonicalPath(); - // set parameter for presignedUrl upload instead of downscoped token - Properties paramProperties = new Properties(); - paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", false); - try (Connection connection = getConnection("gcpaccount", paramProperties); - Statement statement = connection.createStatement()) { - try { - // create a stage to put the file in - statement.execute("CREATE OR REPLACE STAGE " + testStageName); - - SFSession sfSession = connection.unwrap(SnowflakeConnectionV1.class).getSfSession(); - - // Test put file with internal compression - String putCommand = "put file:///dummy/path/file1.gz @" + testStageName; - SnowflakeFileTransferAgent sfAgent = - new SnowflakeFileTransferAgent(putCommand, sfSession, new SFStatement(sfSession)); - List metadata = sfAgent.getFileTransferMetadatas(); - - String srcPath = getFullPathFileInResource(TEST_DATA_FILE); - for (SnowflakeFileTransferMetadata oneMetadata : metadata) { - InputStream inputStream = new FileInputStream(srcPath); - - assertTrue(oneMetadata.isForOneFile()); - SnowflakeFileTransferAgent.uploadWithoutConnection( - SnowflakeFileTransferConfig.Builder.newInstance() - .setSnowflakeFileTransferMetadata(oneMetadata) - .setUploadStream(inputStream) - .setRequireCompress(true) - .setNetworkTimeoutInMilli(0) - .setOcspMode(OCSPMode.FAIL_OPEN) - .build()); - } - - assertTrue( - "Failed to get files", - statement.execute( - "GET @" + testStageName + " 'file://" + destFolderCanonicalPath + "/' parallel=8")); - assertTrue(isFileContentEqual(srcPath, false, destFolderCanonicalPath + "/file1.gz", true)); - } finally { - statement.execute("DROP STAGE if exists " + testStageName); - } - } - } - @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testUploadWithGCSDownscopedCredentialWithoutConnection() throws Throwable { From 988cfcf65ab93ef8e96107d3e0d2fb23c8c29e56 Mon Sep 17 00:00:00 2001 From: John Yun <140559986+sfc-gh-ext-simba-jy@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:23:50 -0700 Subject: [PATCH 16/53] SNOW-1737555: Arrow-shared needs logging changes ported from previous version (#1922) --- dependencies/Readme.md | 2 +- dependencies/arrow-format-17.0.0.jar | Bin 118104 -> 118095 bytes dependencies/arrow-memory-core-17.0.0.jar | Bin 115359 -> 122805 bytes ...arrow-memory-netty-buffer-patch-17.0.0.jar | Bin 35433 -> 35441 bytes dependencies/arrow-memory-unsafe-17.0.0.jar | Bin 10755 -> 10746 bytes dependencies/arrow-vector-17.0.0.jar | Bin 2063573 -> 2063599 bytes 6 files changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/Readme.md b/dependencies/Readme.md index 7b4a4c73c..5abaea2ae 100644 --- a/dependencies/Readme.md +++ b/dependencies/Readme.md @@ -1,2 +1,2 @@ -Arrow dependencies are built from internal branch `upgradeto17.0.0`. This build was applied the AIX fix. +Arrow dependencies are built from internal branch `upgradeTo17.0.0-v2`. This build was applied the AIX fix and the customer logger instead of slf4j logger. diff --git a/dependencies/arrow-format-17.0.0.jar b/dependencies/arrow-format-17.0.0.jar index 34927211360c571aef0929955d056c79fb633270..b4a34e86febd233cb6b3595c7e1bd9ddbea83687 100644 GIT binary patch delta 2320 zcmZuzdsLKV8lUF{8AgL9Ehz~R+2kS3%#2`B(mpZR*MsCANuA8)-_9vcdlBpz}ciM-~K=&FGzVwy&%3ANPiaew@3%xGVAf$8ue&{MjB|pO^l~iZ)tzK-uT> zvE=@HT^3u8H=cf#mA9N29f=?N?d|dw-{A8rUi8n@ZaR}`_<}OVI`v?+wC+s8ri|~a zqYfY4V-d4;*~=?G9Xz)=vL^1Y)~gTA*(Q%I`M3KsO}*Xwrpl<-a~`T!?>BfU?N0v% z<;@-AZ|rnXnT^`2hURAXKJeZ8d9uM^>b`4xZqD7cc>lIrjpAF%lW&y#v@5DryL97m zdDFEov_W-$dQ(~U+wn&MULFg(KML9yUeoq+f;ud)^cpw*6vwTOwZrGg*%hlk6s@TghYY$x3rvSPbUeb{}(0u7}xdy^mlxd@68Pco12Ix{<7cc!Ja}0XD*=>E&aF{B;4W9dHS>mizsbv_@n^~ zil`UMofGFkR=RrvT3}o;c(U}UDc9~|(n}G|pHZeE`G4NWV=gmVjHHw&D?iAOggqKq z!x9g3PDiDNGl)!7>~wr7ee{tPFNB+a#WI!K^ZhDoEBJSUL4~AeB-`xhts)^z~HbltT2;1UU!i zwbM$!<;ia^2xMl)EY9JR2^nKE%;!9pQIRp>VS*9Ww9hjY6n6Y`VaVzV8uCewutR^f z1nnznhdu>@t*#P`IaR?#&c34QIU9;5U3}TOvPo==_FC|UL=4f=bf#4pss(54(Sj%3 zK&XO0gA(^t!49^ind|Lz;ZmCQ$#JxbTDs9HkPF+4aD$H8$nT`Hk1#(M*=>%&4RkpJ zx`|xErF0wN6W+<`YdXvDrD{@8b6vpM)g<5teT1c4N1y(7qfHH6J2PBS10n3Ye%^+c zYiRk=wjDDV9H%OF2b=|dSrI@1RPOE z7eM-$um@`E$mfrvgdMn)QA_+lLyEi9Q_k&AIT&|DMLh-n@Qko4*4C4AE<;TN*y9~W z{R!^QSIIruP)~Omg021ztP#WX6*!Bh98B=A7>uyvDy7S@(9RE#@%01 zl6cQ4n+b|r!2uV0&FI|BYs$d_?VG6SEo-M3nx%=}a+rJ?b4d=?dJTq~NScuiGi}DW zwwdal51L6E;ZH#%DgSXpT{Fe4*!p`1gBDoHg177G!WPmnvW1@K4NPsJ4^Pf+%xWh! zjw(ovUv~3)^0~GsW=!*>epT->HTB(?ya*8G?l|&1{*Lhp}U#8j24uS;j+DL_p zV?6DMqsJyooo*v<{}?@;5JUY5U*vCiI_`v?Hf^Wte(WTFa6#Qk3RTonwFPg(^lvG{ z%yxKP#1U`bYBM4d_h-CmO~+5cfksSHQCdt^Vwi(6B0bj Ui2r8A(SP(3k?49h{rD064{+dh-2eap delta 2209 zcmY*bdsvg_73X{b4A+LjB2p+Qgg!uZ5rSYvF5(1)%9N_ylrnAAr<4cOxjNJUUSb{g zS@_f=rIW5+00|J4giA0KFn}b0QoHI@s0EbTvZ`C>I+5+<`!J>XBhT;r&h7WU?|I+n z8~AwE&5viPXS>;r>?_Ug2P>-}*2m3XF8p`6Dqe4}b5GZ<3D)|lvhVsx{l2^Tw|5g` zQ2WzU&B(E{Z!QvkA0zx$=PB=w`yn@`DBIckn~a@78y>|pzTeolT%EH=^5xiXBwqIq zRe#q1{9JHS;TJng?EDK%#uZ7G9yDE> zP?r5}!;i@=>&6x972~m6d^Ei;b-EqZzU{S3^|0^nQ!5L`o^1Nyfv)Y45t=NFgkf&hWf@oeO9= zwKvY>{`~OD_tWqHHlSivpu&Dj!ob5(xkpoMqpNGM!`za0HSc4U0a5K=7Gis6a!=d;CUm5>5Pghp;{i zow4gYgfRm_7CWE@q|9UKwC#ZpR3Kt|f<+kA2KG#6u-SpPYTykfK9s`?RS?3sF6MBt zigd}BaLIK`9MD}0B7CX>4<;>=$X9SdEkrR<(QI~tCz~5B!rW>KsHvp@!!k38Behg* ze;kKpbtG+70^witfvP$Ryp>4AJk~kM79Z8oTIR3D7&S=PcUHg5MX|;dk)zf0?@Uow z3h$mualra|iuX)66Z6^3bSJ*5L=7I$iqUG=$eiB9N8jFLj{|Co-uezFW%dQy{C{oX z@Qw>4OP|AD&k4e>E>KKKE}OsC6TL6OBE}|?JfuB3ibhg7% zs6ps}HG)tH&*gQ%N`d4xi{Xe_ufebQ!op5!-W~8upAb_zspftuPk-78u>$uH&Ny)e zA_U11yra+nF8EOd(ED=*0tFe77W!o@kA#=_7UNf_#Z>eRUt|mo%VGniV1k8C6ugmS zG2C#{KnmVk$1@3C5Gfc+BG*Kn1 iPk3Xqi7I_nfs-Z(!9Ekrhgf`Of>pM6&(PP1)&Buaq*AQ_ diff --git a/dependencies/arrow-memory-core-17.0.0.jar b/dependencies/arrow-memory-core-17.0.0.jar index a218df5db031bcd0489b98c8c5187f7cc9182bf2..12d9c9116dc1242937aaa40993259eb331662c47 100644 GIT binary patch delta 49969 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%0A@Md89lc0*60kTdQqglk6!7?Sfm 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 delta 42981 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 G(EkCHzriK| diff --git a/dependencies/arrow-memory-netty-buffer-patch-17.0.0.jar b/dependencies/arrow-memory-netty-buffer-patch-17.0.0.jar index 2004a461e05aece44647f49ad08649d27d8c4017..72d3742479bcabb1b0d0002a4feed11dec4771ee 100644 GIT binary patch delta 7826 zcmY+JWl$Wj*7uR(6o+Lg?(S|aF2&soEVk$Z#bwdOb+O{^?(SNg;%>!@mr^L@?R`GH zcW#mo$;>ZvGMRHS|8sJhf^?IDRKG=xb2?1PKZ1yW@a_!)0uch7Dx3sbqiyW4!A%s% zb^o69LAYA6cLW37NRSywht*vD?oh5V-pQ$v=rcGA`d-Ylt$7+XSc23dAie172%(@)=* z-+?Wo!XM*#xWw|?WyA8JZhXU z*y@s=jR)+18~Vs1aljypvWIdZJ7k5_)9noKvi_@;5k&y*6KTN7+v53qVU?0tKY_b+KEA1CEZijtIe_qcMo zmSSUas*h#^lGY%c{FuD5n)8NSOY>S^bXd6U`kEaYhY3kdCC|MvP(1$y1&!3=Uc3(c z=oeW=s0`J*_k8&-$@%rOTJ(wUPY%oF%&~I8R*+=n_iswy4ffbi4RijeBYRzVTJGcI z6l|lCzNS`WWabhpT5@np-C%^tu>Euym$ntB23fyyYt~1%ZeMMBie$Zr%uIpr4A>qB z-{euOFMc9uqK45WNWR>6g57#<0$KoYwP=2ZbU+_M(=6aO4+?LbW56DKTFDIhPLAmB zXj{v4D@G{`hf=wz^ub3hA*c3I7GdzZs0UY~VDWcwx0q6^=~d=WQUXpn@oPG~K7^=r zW%{HKAzp!m(%C>RwWZ-&HdmijK3kg^|D7-HYrkJ+^Pni)F@9j|Jqm_xRS>B=*kss0 zx->n-TRrO6w{r9CerbXja4`gG2;?krMbS@fw=rMq_khuixfc&@$!#B|(X|0rd1W?l zH*&4US17O5eL6TO+8lr9F5w{UP4K1%4mErwmRR(60maiSyGa7Dqhc_x31s*_x&LWo zD&FK=**HI^WnS^Fry*&U6k@*4_YBu{vzTw22rtMyh|2mQZ~Df|d#ePRsxsZy-t`q( zVR~pJWAn2Sc1Lsx7u!XbSxjBbq#8B5p?mS?kz`x3O%QigE>nnHdYe&5yvch=(?;(V zrk{mz<+-j&`bja>B4cipYH$nz>X7%29gnyu=?u;(e7pepRT=Vec0hfgV#UMb%uqfXxN8=^B(7q1u6FXKY1Rv*=?$Ik3aee z^vRdvK@yI$tj>gEN!t6?49=wO;C&M3EN^@uA=h3;6h1lMHR2Go_+jb$w)~gcO=rQE zpr4@wwmEvsch;?bA8!2Z3WExRi3zt%rQtm?}mylFtCgoJQ$GpYPwn~bfc z9IDzQl?a~!$Gi))dt-%owwJZoGi1AzsO?9ezU6Q07JdmZX^^jbn-_k&ulkF&*yRlIj+236*vAMH>aJ$L`Wk97Mva;mr^t z!2!$=gE6*dql+MOqBcYtW%gp%+H06rP#-(>I9| zpxKbD;`^%O4^M#NOX~$c@4Yt8PtsR!$aXFa!XGjBe&X$s(a1j2`j_H2QdHGOfU^gz zA~Tsdq(xgSBcJ3VGuh%V2ePT2ahRgt(=^1;Iq;L+k8E@acM3ifsr?M}?xkO-L%+o@ zd3L}Y6jD!w9(Nv!r11NM=#_G-9@>gs{tec4G_c6QqUcZ$)@`4#1l#-KCXK0^UV`G@ zZ#10lGw~4GypOuGc!am-57jjrb_Zu1`P@M$1%+o%ZExnzjLCjD#hwdBYQ*U9$ZN8) zCGH)5()ft{h6n)x9UWn^!A>9VfFZpG2?4s~%e)PgQY%Z&;vz{%F+CXa}ABmMj9cT&1g z;cj(^JElAkTDZyYl+)F!1IaCLm}v~l-oD|X_aCdCpimITKFGAFN`q{r898xsVO4Cm zIB7517}aKgIj@`-Z=Zu1fwY&=dxr{kJk|ycFVOGuwom7eNXoq z)-mR*3#;`WAaSbB(@dGTuUsLUf4)H!u;Y0 zMZmbkY2x~^=beyj{|*zIaX@q?$2qGDBtWj*MESWwSy#8LC-o>jeLd^h7)NF`)uhY@ z5Lj>+`~cbtaJ3jCD%`{9GBlBmH;W+m_>I#D-X9#j3?&o6_a;MqtG8=|w2l%5kx)lp$Rf~Xq*IxHUW`HH_3 zgJR0UBlA)>!jdo1dlc>i>+dm>z8VjQ2uD6Lihc@wDX!5DglROL(3<*9xmupfaw&YI zne3dKP41d{X+98En^Qd5AX8hY)3_**=0-ex0cF#YX^3VeDM^$kG7xPFKQejHm%Uv9%mWc_F^Z3wAsw2P*rT z*4{Hha7$YBVkII&OexZOWsOtIy3upJu7lHg)6bW*TrsgV$7I#mz?a^@-)_g-?z1Al zy01E(=M=oI(CWh|^ukd6M`J*01XNX-XXO5vmLT{>%Ndf4Or9oH{?TWHI zy$prOJDj5;!sPrT^2gwO4Mrn)$R9R zj~Bj(Lg?+9!7{?gN%E^*;3_u`5;J*}!Zx z>Y`+UBWB6xNfzX?Fd`c%gIXQFw~wj1XXyBPFx0iWRe{h@(q5;Z2HtEP{_zWJqMm0S+X>E zU~!ToXG|`jwH0K=FeXDx;7vI@OKWMp;@igHTYQo(NJ`ttXG&EQ2qjx)XRBR4*HqmL zAtu1ocMy)E!?#F*Y}hON$0cjfW-iG@!$_ggU8yypcwVwmpOc>W?#|L(FUjJi|3teF zrWW{5ndb;D;z~pzeqw(*3})NqLF^;G7Q2-%D;xn<2K*)WN=nj-3bQb>{~RDKKz!L@ z0uTkK?Tt};Rw!GVyIPqw;jYg$8Ads1j?hkQk*8YDf`HR$6{kF~A=r3Q@U6ENY!qs2 z1M*OgQP8Etw`@w{r8^Uidsl3k@}ctkJ%LfZlr$TqZH8DcI?=TshWaip(qHC1b#Ifw zyqn@M#%kM)WTsJIJZV^N_5w$Z@${Obkpf9sCY{SEt*bRtadVR*jUiCDNiR+4WV5f) zdc0KmtTIbi1mtPhFqySE0k26rzl8!Ypd;EyxoOwfe@F42@=|GLUJNmqiVO#LZnDFm zyLklrCu!GQ;#|8Z7+m`d7%YxS*Suy)*Dg4J6T;iHlFG zNHIF-XQ2u-GvEQKp+ZWw_lLv?wnzMQ%HU8rwI$!iTDxf z1&K|_)FJ45 z2+HDw?!r%-j0ObU7EXcuYaPttID>w*%6j~mwUsY_T!j}Y9VSS$qcnW>n3B}OC12$$ z2@fuxU~Rg2Y4PVLrHT9;#;8Wb#vQIa8|i%k6zdqoa-CXFU!c?LhLzI{DQ(S+x5~0C zO|5~Z5SjLHad_Fzqh|`<)qC$uAi(}f07sKRLQUUXb?7XBjy0MGq`RBK7n2wl3 z9&|iz&LiIsbfFHL-Uo5z;huOt*+&ty=1zgOhcK01Mt5{*ZW-M|`Gzxuw-N1>0({G6 z7dj%gt2A(Q^aM9nt8>Au#aXE<$#tijUBQodBbH3UkSk!fA@$0>_ZMhDJ`L!S$Ep(V z8F}46)V^ENH_MLb`R9HW^aPcX@yX)d>UG!QqwBfZ&~IECezpM7;B71x4~Z?}Z!iKe z(#eR;CFH9}jP5Z_W}*0Ly1fd}%I}LRkj-*nYmI>DT=Dj})Oc9joX!pjj=<7ggc~Kv z-TNaoAUGe^(6$oWBB4x`HMW;HA0!?0x91N7G7N5~8#KF+*%^@EoYI$qepJw-*F5iy zSz{Ik-6-8K(Cqxu#VP(_>UOJn1h|zcMuVhJy!CFDy1q1#Y-osY#YV9DCJgx>{#s^kp*YU=o_DGdAa1bgYg*npj{ufT^qBNl*;VvNI32uAdy;wE)Z%&5MF{`^okl0rY zZYMNpKklCHLfnXz4kemaTx;t~Npe>+s;}x32zGbl+D|-1JyZ%U=^ft^DcVD47c$nM z#z&d69aEL==^a-i7fx0w{Hz-f<}-pR4vRjd`U%UE{G|fWY|%iM$cJh&8@A8a{|kuFEKCW z&cC!sXRca?va2KqwtePEbibO5Sn_P^i;-jR_apZf`oCk;bqqBadC}gG&*MjeKD6hr zsL{`S`&fvGtGyr|LLjb8E1C9IWyv!f7c>X$!)##UV+18_sy<*N;>;2k2m}!S54^q zK&_(=atj&5{~IE$`4?5$sv5qtB0y3m(cECL!X`u@++#j4xvgfsE1KT9YCClHnkzzR z`Qnpd`=Y>-$6;KC9^X<+`HbnbJHx7HZOjkxd+8Rh7S-hGX@LNPkI>jN_k3%^6VZ1@ ztPgwMMmdS02eThf-^nYDUQ8dE5SGq8ilayhv?qdj8&l@2vi!@ zY4i9ZXI(S;TybQFwz*1f52SjhS8n*Vqg-hV1||xe@$&u!uzd1{l~COILd2}M(#OM) zMN$TM(POfFL@{v+P%WLiZaGMHEe<2SAl~4_*V2PGQ_@V>C}IyXL)s_Z*+c}{?pW8> ze&P}Qh`NXX-6NcA#YC-4fdwzrnm+ZL-+~FjTC-5Z|5ey62GHY$f7v1u4gvxP0vr;_ z1l4}%1;XAi0V6m#b&H9U?4`q{Q0&=S-qM&mA~1Z2GdV*$dk3ht`i}rx7+&&R{EOSp4@ob-t7og*~zvY?&5s~is&tY$`y%*RKKVv$|9;ajHDx&1=Ml2zywdu2>%3p88M zU1Vv0E={l|o1SapYd7menzdQdXFjHpwW%eM35!=nH!DYRu^h^Glxp$nxN%Igs*DPR zYo{u_Jc6Pbd;KNCiwr7sE|V|`hI15w4?i!CHL5r{*CxqD7cyCZl}=nA`NBIymXcdB zlm0Bt0^H7miqht5E658^V{WYtS)r?$oGhgUMC>009HR(6EfTR@VuQbp%w7gndQJFsruE)L%Ma)@M?M?O@8&(1FT8>bxsRk_YP{#b`l<31a^2Ns-QJI81l zZi|`ZAy~6bc~STXa49p<_VapFF-D>KMB3=`w#{7b%84^5a0}j-2oNj~gBIDcCcSFe z#B*~vTYIdHD}hk_9G!>33G`dW!fmei&Iu_$fDDV#q) z5IW^@c8QD9cw2po(!Zl<7BO-a6Pxsuu$Iyz63BD8y1YD^qm27~y6G{c37e1&SP?kt z+q19rba@n6Hf=m%F}-h;gO-1q?|H!EwHeS_q={Qo#zRIc(YfD`aq*93g>zz^_FF)P}kq?2dWr(kD4C39HuND$w~yJ6h6#M!pl zr@$xZ^yy~Xm*`gKXqnGvDHQC~$EVtr!I?Jp;_L}81eI}<(QwEtLrqnM7F3_awtDo0 zru5S3<^01EeEezmwf#r;6y=6G?6WpM=`J!D^RfIoqA~o2m9~D?qcJ`1bJS&!n-}wl zG(_9`rY8$^%q+cI>iARCBiRl__~NcN@>uzd$2}>ThW5s9O+lpi&)L&s5(WVw{%?wo z1T=*+Kh|ga2~DO(C1`wh&bS(`3eH=GF_DX!`g6nLU#nWDpI;J1z;)vBvxiO}^)9qp zIIh;gWzCM?b$$&pfmK_W{@rno>bQ^JpbAf?r9gfbv2D&%yUJ(*Tc+|BKnY70Mkh<{ z7S99X*Be{`ZY+>v+OzKz<iP?6Se}7g=E3ix}OJd!3 zh<@C~e0Vj1sq2sARYc?J4CK{BLpxb}OECirStWnGgZ2{akPf2DOZdmQBfW7ccz}WY zkT;YwO+hKaRV+TKwbrSfnn$HA!>SxJKF*_6A;^FGzf|s$L3g7n&1cXuD)|llYk2BJ z_Y}`5$RAN!gkl&fy`Ea)rYPvq-<6Rc%d%sF4v6=+F=l6yP+-GVr-5G-!{Grpq^OyugH%SI8koF~cbW27jX(iXMJLi`^-y z-)Fdkz1@Ym*vIE95wX)Wup=4QMakXwuIJw8%Zo!RW2arz)FUl&w1|0T1fmZ1cIJf8 z$q{~ks_fVRE+2eYuh*zM5g8UY!4#Q=R#SE$ZwD`jHoEp&WC)NIg^OwJ9!fliPplZ+ z%S5b`%-umGe`5_|u0K6PWFxi=A3Q;)miatldnfmFN~Gs6vxc%_N|MyJS&cCu(5&?m zLQHHaH^9NMfdC*X&qm+THsH1PqgnSY^X?F&HkSHP>!WIdwIM6Gg*9)w7N6|9IZ^6j zG3Vqmi0FT161p;vZbs1m3mWp*`1&k#vSt-0jDOCqy9&E~xfI%>}Ixg8O=lsFx@iL zD0Fb=t^VPe8(KA^fQWJO=v#+@CwsJmQPn>+v;N2m51^c5l z2@gGJ5^}u&*n{LERQd1lCW0wdK|<*EbM#g|Y3Hj`AZ1^i{DYCm;k@3RadVE&^XQ&g zf_%hH!d+7=0UL>1L7XYp*M&cdUAteVa;j|;2SAZEoSo(;5d56bhOOM6GIsdu(a%pwYq~3}|{#y67qWPYPbN z`6c~V!0o_=v9>>#kBVy=`8-{awQ9Zc!OSoIFF`E$)q^*~HUk}#A$T@|xRRaS^q%Mu z92&>bE6aaw@Q_0|6}%+Ku{bdKJkL+#zNs#mR{D{1ibX}BJey%wC#9(#lK?joKl%y2 z6-5T8NB;0HY~D0Q1o^+g`2XLkaO`kx#8J3;xDHY%6}+5^4!#!7inPiAe_`N&M~CCV zrD0@e1R22!gcoLZ9=z6Jx3|KF$Gzku|_{8zX~_-`u?ZczFUl&s7IH;up}|DOW? ze=&sO;8)duobbPN69`}b2mb%cDG&ic{C_~8=08vpE*pvW?tjJKf2n^~;VzLzNHdo3 zIZN*U7=~j-ks(=o!O26Z;R;b~626roM zwb>>mC++#llC`45m)4i3mi^QcWqza!38d35GVqApwX;O183u6A(38dZMxxEBZA!Y{ zF_3QYP==2DD5|1x`cEmhQpkd50y2HT>wf#DZ$S1!0GZwfhfKx%S~z&*tb-m22BDkH9}g z;DgX|+&+7m{MFp}LA6uoBw9E6M9PM;WgK$=57PP%xp(#1)Y=;;;PB4E_qeO~L&jv6 zK^B#>G#Bxc+kVgnr@rbw5}SCfiCxrG-&by?yBMWCFj^c^zo3PF3IS_m%pE5cax^tN z4|kM&@hb#_XBu6kdvv=H;Alo0O(=pj-+sBR7kUZk0=pX4r)!5OZu7ou2D(uc>+_Z} zb>;@iO%nZy0XZ2QsWIiLFh`@;<#5L#0f&bbY{i2c#NYfMA9qsXQFVrLWH(tE2bkLpvycuSUG^ zjHCPqA#I9$&5xcZ0d4J>gf=HE1wR)AzZ(9EO97 z_b1;7M}Xw`rTe#UJ?vrb5JdEb{D%&SP{bCN>J-%Y9%Xj4MKaY5N0%iTS8(*QvBB7n z!1%5z5!W9gMhRo+Wka8<^nNw=_nfypceY;u`&0PjK)S4ctplP6O=o`8dY*n&bSI+^ zZ~|SIziaCWTT41khP+}`en~j(m8{^kGn#QGJV6Kspy9h0pT&~MKHk&;+wM_b@r)*1 zG}s_VsECP|q>$sP#o*s=BjBbX0&UB*vvGVADQ8ZiP*-@D3gA&yx zK5iR>)on=qQgCVDv3if+jZBDcOKUGGG>hJOw>5S-nd=rI@hedu7Z@&&OUd5U_=<1b z^U%BL6!8bREt7g@{`Ol5n<8(TWJ0BEQp`BxAVkmQg1#1i8QlNM< z+CX7XdL7H1i1%ckaPT6|pW&_`Zk7<)@E+d)Lb>JP;Q5Lz%o+Y< zG3wEg)GHSzB(Q2O&5YNW2cz8`utv!&b9Bag;}2ZfN`3!F!jJBGfGSt$(EN}457err z?HifHd6z4mx6lmaro6!y&Q}Ga*p)00u@F{%=3{?_OZ4jsYEuCI;7N8EY5f{?NwBahbB(!8Sa%S zu@|QP5W_PAALo)!f3YT7c8b-_|pTg)Cq7)cf+(i+*YKgho6#E;_cxX)kxH z>AmK%L6gPtc$ln+hu<2!(f^!SoH%Xba_4$>%Z8((!(}YFlJk&opDeh-H4XlLbnIa0 zS>5pvdN^c(7vP&TGXW0(oVgO-{!|d@ysZ~-TlQp;OYmHd@!S)t@LIBAwXVlpX^tQF zkwYgd9;MBsBrEOoH=ceTx_>RGQ{xqxn++-x`&L3ax2k^4EpCA}_9=tF%lSe;h2gW@ zBAYXOvd_GapbKdG_YjKv{o2#4;Rh)lZ{Vn<`140Uj;gkSb>==}2s^Ag8b za-5PuLs~`LfRh!-^ME_~U6b3VTP)**1EL&2{H|v+LHr(ZHl&9YF1hHQc&H;iiIQ-P zWGg!5lJ%R!NI>zPwB}R6H?ddq3mh)x7s}39f5$PrV*NPhoG6J>^B6C3LyWmZvT04) z)$|wmUo6c!>hi4gQSs`3{IUzV6q`(_C%BZGum^hp|E^xDA@5c(R2Uc%D5ttQSf2pa zf*J$MOaNKmgc@Ox9m{i{UJn;1yvDmxSOGzRXX1ht%gMnZsHMk#(Lmin8*i=>mM*>& zuw-vo^JrGnx@fTetM=NaCOubJc-1jQIU)E@Pw$r5^hq}~W42RP$%A-1{YQvBTYR`HDv5f(kcSC7oe_9mnNU+oQSez+i* zWuRm;$A0v6#RP9nLc!-IxWKPh9Sq*!*emn>9-fE4D6NA{hSC`~}L$rOc3|-=L$0WaM5U-IXEmB?dLywnG0w~ z$yj`5PjXb_XOhT4(s|T70|of4qNGsLMLU86O^EF$cgIO^D~RlkhU5+9pMh11iaL`v zNC~>EHU_n)nJr-jYvRtepHRac(y(cWK-8JF{H2}i+D-a)Nec;J4oS;ff}tPJZ9HQi zi9kH%gZSTgY%9s#`bFDiHTR9YWO+3`md;zH7b8lDKh|`#>!T@(Gw9HoQN!Yk)<`I< ztRX$Kh3o1H)U^iX-Ksc`Ls^1@Jkb1OWi7&{e zNP(Xj#o%m{LaYPV5cSk%cB=V|1+j8^#c2*2BKjN+&&0k0n3%-nYh)S|+XqcQ=@@0% z@q|hTUJUsc#!#(+7dl>xoCpGuK2DoJY{nf-oZLZ8z#HSPs;^{!-feS)Y_(NJ3d1ll z26471SBarPTV~TrdlXh#CXLG#wU-q`ar43$r9SaWlWh9(=|*p()o3Z`s4`1S2(GJo ze)8pXRWl&6$Pk`R zwUCceT3As^uIG|zgf=dg=<$}j2PuYUgDSaJ%~Z!hyp{CO`hcR1aSvr(*>&h;7 z>RLVWs8!_#yoF-}_kLb}J1#z&dYTfsmted^9n2(F9m)pDt2MThWGwg`x6lN7E-7XA zU>ig5eCP-V2hbfM&Jul~TH<&AZTnhD-yk|UoE%S;`Lydb#^CmB!J>5U$HiL;(N!%$mE=7ziRG}G?efCcPFHG~#} z^`6ibAiezLKpc=BRGpDb%78g02j*5|sgDKYWDSDa-Aznp{U*_Os7RUO*WghQzd;l7 zsqa3NWhpJjr)*G9B7Z`Va4u6*T)N?-z;BXuPsgE9mXGVQ96MI@iR*Ra<|cb&iY87w zR#&BRzhxYOB^Q5tcm*;NORqcMAib+B8q~<{M>tJhF%uoV$bCQ$<;whgTEwajm#hV@ z`J6+a85BV?B;rMW$zn$qoX<{j~C$7e* zcjocfMK#*{qs&}wYOn#+yVG6~`Irry6WdLq4yvv-#5Ewaqd)k0@A1-+j!xYhLbH8O zytbRbp_xvADT^+e$;}j3L&@gtF!#@Buryj1$E|cz(02AM7d9SIEhEPH+lc z=KI;}^eFn*K;;9h7ZFUqHCIH>3W>-(P@i*G=-A1J!hpiDqzo^DYgdNI%*-z*3L>NP@ue`i!c1xr5h?>yq^qL!jtc{8S?3&kT|A>;aD7O6Rt zrO6FIM{4(14TmOKgZY<8oNP6&_pvPdWo@e+AQ7bJ&oY8ZF@AaO)(OD!;EO|T3$~>B zvLiP^%O6Ep`Cy9PijZC4FXr!rr&*nT-_H*Ps4{=f8lW23iNyApM6QF63{CZ0NUP`5 zWJBv_kI%xp3NnY0(-zZ$upS4RNM+X5=Vz1l`17=Kh6jTJI8Q&MavDWSG5_9ms;XA9 zxO0AHzK*KH)NCEl96Rw~xTH1bKMHyOxFj3E)+bFeQE(B}noGs;&EZOBV;ct*xhO)abNpnVrCq?B$`Jz_E|#PlM{p+#{(P`Bv8N6(Ws~ zR8BaHLH~(0_mrC3-i{}#$&fSjEd+6_C!FMX6H?JqtFbKAUX?&!>Lle$So$UHX?Mp! zfOzmRzxl^zwAA#e)T+W)WCM}=;^0Z!^AR(rk}F#HyU7bMo7dF7hV^2n;^vdZ zqYi)$61QY57|7E7CqpXXHV2D^UUU zxKHL3qE8lekUwrnm}%HCbIym?n@G==e&s0L_FqkeliQUjDiB1J6n-Ls zZWt?`_4v^gWS1`Ps#gyaSL4{i(`FCo9liO*r&mRa-T)flZO#XZ8Eg*gp?LkbyV;ZcrkF(Yqu>n95;_=Cb$UV`6=Cj%*sVq~X=AdVnGX2@EQ)!{m4Jnl<+$MwKn zYk#AtU?;d@N_(0jpgwHjVscG9TpCUl2Sv3Ytddz z5Pp+g`jvwU!fuooepLrAXRzF8c|ep)&$n*T(AYgM5o#C+HbE zqKhr{OU6SUbU*xCm1v98^LDb#PbMnw zoGO4Ofh-S5hUETFJX#L5@7*yP-4S1Xi4|{A5({j|O{9Qznp6JaadTNKF>Rv(F8fN| z;U2f_+U-w_7TY6E&moOB1FYqW$gg!8JvV5I!Z-emy4rydO0v-cv@{7VvZGT*!4P}1 z_RH}$`H<*j8nMB?Ev)ycVdM;W6(SVxi|FXGS$!cUTEX80JLiwS^!k8G0DI-+rC_dg z_x;kR8RGjO(V=5R5rppC_x~?p<5qpDoPvje=|_iwVf(9!n8EB8j%FSnIl3>pxVm_6 zQ9uS_M1Y%|0d@rfGa9p=;VFRXA4eFvPo!P5X1)=jW(W+p_+2YO+fR@8sTIk#oX33}-Hu6eBV;p~e(i47J$&rCIA7(EY+hyS5p=#U7DBKOSB=}8tjb=j zhJ$>th`;%!#PC=a&!m3C}gKK3#;QkV;P(raM6ms;%IzzNznrM=(xs*h+ zvgT(?FR|y5SLei|{U)^cGX;Nvu!YS;^1HfqD427~#0q_JlG(z~#?g7uDLTTbYZd6> z{4Ju%^P8RAB7xiwD{&p6iR|aVEu(u7=u`ufcmC*W%0v-vn4Cb)GvBsER*_X(Q^D~Wn5^_F1k2xDt^&zKmcQ>L3?KPd1MTl3s!g+lgDU1Ewo z8dzmRALfVPz6L|0oX@QlB)O%8r}Tevw!kLwgx2wLVccTF4Af$2x5Q$EuBieUoz?a~ zmDG{F^>2Uhck~uESjn|9K9y2w3*7NL+?p0UTL=9#7MbC&42(4+HVxfU7;ITCah`ox z=I-fh10-tsnGQP0JY?wSTgC`he!H*$q%5B4+|uCC9#J(4`%_>h{&-4%T5PORIRK{+ z2?->B{g6RM0!lz#(Y0P~lfQ20*f{+~vRGrVX zSM^_`&Vp14+4O_l{rC9tv(juffB8<*<+G$KZ4ojlN(a+OQ`)Edv9`HQ*o`b1qu4WU z7Uhw9bJdaDuQ#xyGhfCpToRrj3W9ZyuXJdt@v(*BY5!4WTE$R@tspB_ zJU%zYjAjw6_`V=0Zz>gi0!I}=uj0t-&_5uE;y=P$CqYOe#55~1zB)s$`fQOT{`iYV z9S`CaLFY~TLR1sn&z}jaN$#++VB_6#wGdHmOyhN{`Y0Pn?7x_=MrFEb&R+n zHbr>|MMVs_le(uA@D?Z&SZV)iDbzzHtq%G1Z2bg8+LFHXT7E=mjpQ{Mah+b)dIc@t zfs8ra7Bmh@)59J&5_Exxm2hP&VGfmWs#-u)?T`u(G;(?EN|%AmsRI(;TlgqjApPLn z+nUlL1>D_Fb$BHfIY^O(aUxS_xDnTjK0QE!aLK;|nx@T9h>^Kt`f|Dd3c++}Z=f1@ z&UpcZ71VYBCVB;GIF*GAYP*F4c>4>LYtQZhNu&W6tSepCKE@2-nTs_h*>75uRKHTL zo+q<;Y^(+RT3qbgWlB2mj{EF*QDJ1*QQ0eh?pQaWy(61U1#$S{MfLcMdEk+ta^g&4 zn9Vh22XpIZ<}yW;TuAq!rG<^eJHCK{Cow3+pSGbWBnlZuC`He$Jb2~N{@%`$+B88MIT?0=a`PVRn)awlTXugn0X>Xnt=F z*QGw$QxF9g17AvITvE-@mxCkbE+mE8sdj8WdvuguZ=>X@b-4QU9|w+#Bo0gA4~rPX zkgVqbnGf-2gHQKvI|~hg0%u@G2uUtrlv$PsnSi)hAEu5_KQrfhwC7Lfpr%mF`1A82 zd#H(JeNU9Y`Qo}Kv{u*Cpx^vq>}jj4s*NGU5_fK=M_WhC?qErZl|IYkpe<|nsL*iI zZ_LA9QH#6^i&;UKAnqT)%j7txm(fkl?Bc_mPdJd6#Hc0N?Wl;qr-x^N?H=F z64NTf?d=4T<@im?VHEhU)8m(S>7$BeppZRz#+y<#MI&)8yT2vx6@DvrWSq^A`kQ=k zv{$&H*H>J0VSIw7E3EDA`dIzrM|}=oz6@gQ*2A9}-7aN{=b3++JtV4)wkVJ>8+I1` z=GN&DrT73BJ)yE^*Vpg`U|4L0D23i(_1wEp}`l~PhdliT7LeZl1VDq3>QEGr*d1!JpGjunKknCUM zy8lD}J&Qv-75)Q>MGKJqUy=XU8%qLE^nr%@2t$=(u*m+WZy>_J$o=biVj#38oEpj& WM+@DEWdqQK{O_w6!fwd_0{;u@j;(_L diff --git a/dependencies/arrow-memory-unsafe-17.0.0.jar b/dependencies/arrow-memory-unsafe-17.0.0.jar index b9897fe470dd93a95c91cb3dfbc6c1b00dbea9c7..65aac2a7bf508ab8c819bd2a71efc3ff79db8671 100644 GIT binary patch delta 549 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 BNu)TQ(qUzt2S(RM*(s+r9K` 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 aygVtikR^>%=&!os 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 delta 52235 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 From 6a004ad614e16d609300dc416699d807ef2506c7 Mon Sep 17 00:00:00 2001 From: Dawid Heyman Date: Fri, 18 Oct 2024 09:30:43 +0200 Subject: [PATCH 17/53] SNOW-1732907: Change custom cloud storage header metadata handling to be case-insensitive (#1919) --- .../snowflake/client/jdbc/SnowflakeUtil.java | 29 +++++++++ .../cloud/storage/CommonObjectMetadata.java | 10 +-- .../jdbc/cloud/storage/S3ObjectMetadata.java | 3 +- .../storage/S3StorageObjectMetadata.java | 3 +- .../cloud/storage/SnowflakeAzureClient.java | 21 ++++--- .../cloud/storage/SnowflakeGCSClient.java | 61 +++++++++---------- .../jdbc/cloud/storage/SnowflakeS3Client.java | 16 +++-- .../client/jdbc/SnowflakeUtilTest.java | 43 +++++++++++-- 8 files changed, 127 insertions(+), 59 deletions(-) diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index 1485249b3..05dad6292 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -4,6 +4,7 @@ package net.snowflake.client.jdbc; +import static java.util.Arrays.stream; import static net.snowflake.client.jdbc.SnowflakeType.GEOGRAPHY; import com.fasterxml.jackson.core.JsonProcessingException; @@ -32,10 +33,12 @@ import java.util.Optional; import java.util.Properties; import java.util.Random; +import java.util.TreeMap; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import net.snowflake.client.core.Constants; import net.snowflake.client.core.HttpClientSettingsKey; import net.snowflake.client.core.OCSPMode; @@ -53,6 +56,7 @@ import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; /** * @author jhuang @@ -835,4 +839,29 @@ public static String getJsonNodeStringValue(JsonNode node) throws SFException { } return node.isValueNode() ? node.asText() : node.toString(); } + + /** + * Method introduced to avoid inconsistencies in custom headers handling, since these are defined + * on drivers side e.g. some drivers might internally convert headers to canonical form. + */ + @SnowflakeJdbcInternalApi + public static Map createCaseInsensitiveMap(Map input) { + Map caseInsensitiveMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + if (input != null) { + caseInsensitiveMap.putAll(input); + } + return caseInsensitiveMap; + } + + /** toCaseInsensitiveMap, but adjusted to Headers[] argument type */ + @SnowflakeJdbcInternalApi + public static Map createCaseInsensitiveMap(Header[] headers) { + if (headers != null) { + return createCaseInsensitiveMap( + stream(headers) + .collect(Collectors.toMap(NameValuePair::getName, NameValuePair::getValue))); + } else { + return new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + } } diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/CommonObjectMetadata.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/CommonObjectMetadata.java index 93646e104..c3602fcf7 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/CommonObjectMetadata.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/CommonObjectMetadata.java @@ -3,8 +3,9 @@ */ package net.snowflake.client.jdbc.cloud.storage; -import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; +import net.snowflake.client.jdbc.SnowflakeUtil; /** * Implements platform-independent interface Azure BLOB and GCS object metadata @@ -16,11 +17,11 @@ */ public class CommonObjectMetadata implements StorageObjectMetadata { private long contentLength; - private Map userDefinedMetadata; + private final Map userDefinedMetadata; private String contentEncoding; CommonObjectMetadata() { - userDefinedMetadata = new HashMap<>(); + userDefinedMetadata = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } /* @@ -31,7 +32,7 @@ public class CommonObjectMetadata implements StorageObjectMetadata { long contentLength, String contentEncoding, Map userDefinedMetadata) { this.contentEncoding = contentEncoding; this.contentLength = contentLength; - this.userDefinedMetadata = userDefinedMetadata; + this.userDefinedMetadata = SnowflakeUtil.createCaseInsensitiveMap(userDefinedMetadata); } /** @@ -41,7 +42,6 @@ public class CommonObjectMetadata implements StorageObjectMetadata { public Map getUserMetadata() { return userDefinedMetadata; } - ; /** * @return returns the size of object in bytes diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3ObjectMetadata.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3ObjectMetadata.java index ec54508f9..38f20cf65 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3ObjectMetadata.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3ObjectMetadata.java @@ -5,6 +5,7 @@ import com.amazonaws.services.s3.model.ObjectMetadata; import java.util.Map; +import net.snowflake.client.jdbc.SnowflakeUtil; /** * s3 implementation of platform independent StorageObjectMetadata interface, wraps an S3 @@ -28,7 +29,7 @@ public class S3ObjectMetadata implements StorageObjectMetadata { @Override public Map getUserMetadata() { - return objectMetadata.getUserMetadata(); + return SnowflakeUtil.createCaseInsensitiveMap(objectMetadata.getUserMetadata()); } @Override diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3StorageObjectMetadata.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3StorageObjectMetadata.java index 3bb209c48..853d461b5 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3StorageObjectMetadata.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3StorageObjectMetadata.java @@ -5,6 +5,7 @@ import com.amazonaws.services.s3.model.ObjectMetadata; import java.util.Map; +import net.snowflake.client.jdbc.SnowflakeUtil; /** * Implementation of StorageObjectMetadata for S3 for remote storage object metadata. @@ -26,7 +27,7 @@ public S3StorageObjectMetadata(ObjectMetadata s3Metadata) { */ @Override public Map getUserMetadata() { - return this.s3Metadata.getUserMetadata(); + return SnowflakeUtil.createCaseInsensitiveMap(this.s3Metadata.getUserMetadata()); } /** diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClient.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClient.java index cdf303bbd..4bec46ca7 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClient.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClient.java @@ -4,6 +4,8 @@ package net.snowflake.client.jdbc.cloud.storage; import static net.snowflake.client.core.Constants.CLOUD_STORAGE_CREDENTIALS_EXPIRED; +import static net.snowflake.client.core.HttpUtil.setProxyForAzure; +import static net.snowflake.client.core.HttpUtil.setSessionlessProxyForAzure; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; import com.fasterxml.jackson.core.JsonFactory; @@ -41,7 +43,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import net.snowflake.client.core.HttpUtil; import net.snowflake.client.core.ObjectMapperFactory; import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SFSession; @@ -154,9 +155,9 @@ private void setupAzureClient( this.azStorageClient = new CloudBlobClient(storageEndpoint, azCreds); opContext = new OperationContext(); if (session != null) { - HttpUtil.setProxyForAzure(session.getHttpClientKey(), opContext); + setProxyForAzure(session.getHttpClientKey(), opContext); } else { - HttpUtil.setSessionlessProxyForAzure(stage.getProxyProperties(), opContext); + setSessionlessProxyForAzure(stage.getProxyProperties(), opContext); } } catch (URISyntaxException ex) { throw new IllegalArgumentException("invalid_azure_credentials"); @@ -273,7 +274,8 @@ public StorageObjectMetadata getObjectMetadata(String remoteStorageLocation, Str blob.downloadAttributes(null, null, opContext); // Get the user-defined BLOB metadata - Map userDefinedMetadata = blob.getMetadata(); + Map userDefinedMetadata = + SnowflakeUtil.createCaseInsensitiveMap(blob.getMetadata()); // Get the BLOB system properties we care about BlobProperties properties = blob.getProperties(); @@ -348,7 +350,8 @@ public void download( blob.downloadAttributes(null, transferOptions, opContext); // Get the user-defined BLOB metadata - Map userDefinedMetadata = blob.getMetadata(); + Map userDefinedMetadata = + SnowflakeUtil.createCaseInsensitiveMap(blob.getMetadata()); AbstractMap.SimpleEntry encryptionData = parseEncryptionData(userDefinedMetadata.get(AZ_ENCRYPTIONDATAPROP), queryId); @@ -447,13 +450,11 @@ public InputStream downloadToStream( InputStream stream = blob.openInputStream(null, null, opContext); stopwatch.stop(); long downloadMillis = stopwatch.elapsedMillis(); - Map userDefinedMetadata = blob.getMetadata(); - + Map userDefinedMetadata = + SnowflakeUtil.createCaseInsensitiveMap(blob.getMetadata()); AbstractMap.SimpleEntry encryptionData = parseEncryptionData(userDefinedMetadata.get(AZ_ENCRYPTIONDATAPROP), queryId); - String key = encryptionData.getKey(); - String iv = encryptionData.getValue(); if (this.isEncrypting() && this.getEncryptionKeySize() <= 256) { @@ -574,7 +575,7 @@ public void upload( CloudBlockBlob blob = container.getBlockBlobReference(destFileName); // Set the user-defined/Snowflake metadata and upload the BLOB - blob.setMetadata((HashMap) meta.getUserMetadata()); + blob.setMetadata(new HashMap<>(meta.getUserMetadata())); BlobRequestOptions transferOptions = new BlobRequestOptions(); transferOptions.setConcurrentRequestCount(parallelism); diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java index d907973ac..003d894ae 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java @@ -4,6 +4,10 @@ package net.snowflake.client.jdbc.cloud.storage; import static net.snowflake.client.core.Constants.CLOUD_STORAGE_CREDENTIALS_EXPIRED; +import static net.snowflake.client.jdbc.SnowflakeUtil.convertSystemPropertyToBooleanValue; +import static net.snowflake.client.jdbc.SnowflakeUtil.createCaseInsensitiveMap; +import static net.snowflake.client.jdbc.SnowflakeUtil.getRootCause; +import static net.snowflake.client.jdbc.SnowflakeUtil.isBlank; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; import com.fasterxml.jackson.core.JsonFactory; @@ -62,7 +66,6 @@ import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; import net.snowflake.common.core.SqlState; import org.apache.commons.io.IOUtils; -import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.HttpResponseException; import org.apache.http.client.methods.HttpGet; @@ -310,18 +313,14 @@ public void download( outStream.close(); bodyStream.close(); if (isEncrypting()) { - for (Header header : response.getAllHeaders()) { - if (header - .getName() - .equalsIgnoreCase(GCS_METADATA_PREFIX + GCS_ENCRYPTIONDATAPROP)) { - AbstractMap.SimpleEntry encryptionData = - parseEncryptionData(header.getValue(), queryId); - - key = encryptionData.getKey(); - iv = encryptionData.getValue(); - break; - } - } + Map userDefinedHeaders = + createCaseInsensitiveMap(response.getAllHeaders()); + AbstractMap.SimpleEntry encryptionData = + parseEncryptionData( + userDefinedHeaders.get(GCS_METADATA_PREFIX + GCS_ENCRYPTIONDATAPROP), + queryId); + key = encryptionData.getKey(); + iv = encryptionData.getValue(); } stopwatch.stop(); downloadMillis = stopwatch.elapsedMillis(); @@ -355,9 +354,10 @@ public void download( logger.debug("Download successful", false); // Get the user-defined BLOB metadata - Map userDefinedMetadata = blob.getMetadata(); + Map userDefinedMetadata = + SnowflakeUtil.createCaseInsensitiveMap(blob.getMetadata()); if (isEncrypting()) { - if (userDefinedMetadata != null) { + if (!userDefinedMetadata.isEmpty()) { AbstractMap.SimpleEntry encryptionData = parseEncryptionData(userDefinedMetadata.get(GCS_ENCRYPTIONDATAPROP), queryId); @@ -499,18 +499,14 @@ public InputStream downloadToStream( inputStream = response.getEntity().getContent(); if (isEncrypting()) { - for (Header header : response.getAllHeaders()) { - if (header - .getName() - .equalsIgnoreCase(GCS_METADATA_PREFIX + GCS_ENCRYPTIONDATAPROP)) { - AbstractMap.SimpleEntry encryptionData = - parseEncryptionData(header.getValue(), queryId); - - key = encryptionData.getKey(); - iv = encryptionData.getValue(); - break; - } - } + Map userDefinedHeaders = + createCaseInsensitiveMap(response.getAllHeaders()); + AbstractMap.SimpleEntry encryptionData = + parseEncryptionData( + userDefinedHeaders.get(GCS_METADATA_PREFIX + GCS_ENCRYPTIONDATAPROP), + queryId); + key = encryptionData.getKey(); + iv = encryptionData.getValue(); } stopwatch.stop(); downloadMillis = stopwatch.elapsedMillis(); @@ -538,7 +534,8 @@ public InputStream downloadToStream( inputStream = Channels.newInputStream(blob.reader()); if (isEncrypting()) { // Get the user-defined BLOB metadata - Map userDefinedMetadata = blob.getMetadata(); + Map userDefinedMetadata = + SnowflakeUtil.createCaseInsensitiveMap(blob.getMetadata()); AbstractMap.SimpleEntry encryptionData = parseEncryptionData(userDefinedMetadata.get(GCS_ENCRYPTIONDATAPROP), queryId); @@ -1121,7 +1118,7 @@ public void handleStorageException( // If there is no space left in the download location, java.io.IOException is thrown. // Don't retry. - if (SnowflakeUtil.getRootCause(ex) instanceof IOException) { + if (getRootCause(ex) instanceof IOException) { SnowflakeFileTransferAgent.throwNoSpaceLeftError(session, operation, ex, queryId); } @@ -1181,7 +1178,7 @@ public void handleStorageException( } } } else if (ex instanceof InterruptedException - || SnowflakeUtil.getRootCause(ex) instanceof SocketTimeoutException) { + || getRootCause(ex) instanceof SocketTimeoutException) { if (retryCount > getMaxRetries()) { throw new SnowflakeSQLLoggedException( queryId, @@ -1278,7 +1275,7 @@ private AbstractMap.SimpleEntry parseEncryptionData( /** Adds digest metadata to the StorageObjectMetadata object */ @Override public void addDigestMetadata(StorageObjectMetadata meta, String digest) { - if (!SnowflakeUtil.isBlank(digest)) { + if (!isBlank(digest)) { meta.addUserMetadata("sfc-digest", digest); } } @@ -1355,7 +1352,7 @@ private void setupGCSClient( private static boolean areDisabledGcsDefaultCredentials(SFSession session) { return session != null && session.getDisableGcsDefaultCredentials() - || SnowflakeUtil.convertSystemPropertyToBooleanValue( + || convertSystemPropertyToBooleanValue( DISABLE_GCS_DEFAULT_CREDENTIALS_PROPERTY_NAME, false); } diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3Client.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3Client.java index 3b33b60f0..bdede5843 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3Client.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3Client.java @@ -5,6 +5,8 @@ package net.snowflake.client.jdbc.cloud.storage; import static net.snowflake.client.core.Constants.CLOUD_STORAGE_CREDENTIALS_EXPIRED; +import static net.snowflake.client.jdbc.SnowflakeUtil.createDefaultExecutorService; +import static net.snowflake.client.jdbc.SnowflakeUtil.getRootCause; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; import com.amazonaws.AmazonClientException; @@ -368,7 +370,7 @@ public void download( new ExecutorFactory() { @Override public ExecutorService newExecutor() { - return SnowflakeUtil.createDefaultExecutorService( + return createDefaultExecutorService( "s3-transfer-manager-downloader-", parallelism); } }) @@ -379,7 +381,8 @@ public ExecutorService newExecutor() { // Pull object metadata from S3 ObjectMetadata meta = amazonClient.getObjectMetadata(remoteStorageLocation, stageFilePath); - Map metaMap = meta.getUserMetadata(); + Map metaMap = + SnowflakeUtil.createCaseInsensitiveMap(meta.getUserMetadata()); String key = metaMap.get(AMZ_KEY); String iv = metaMap.get(AMZ_IV); @@ -481,7 +484,8 @@ public InputStream downloadToStream( InputStream stream = file.getObjectContent(); stopwatch.stop(); long downloadMillis = stopwatch.elapsedMillis(); - Map metaMap = meta.getUserMetadata(); + Map metaMap = + SnowflakeUtil.createCaseInsensitiveMap(meta.getUserMetadata()); String key = metaMap.get(AMZ_KEY); String iv = metaMap.get(AMZ_IV); @@ -611,7 +615,7 @@ public void upload( new ExecutorFactory() { @Override public ExecutorService newExecutor() { - return SnowflakeUtil.createDefaultExecutorService( + return createDefaultExecutorService( "s3-transfer-manager-uploader-", parallelism); } }) @@ -821,7 +825,7 @@ private static void handleS3Exception( // If there is no space left in the download location, java.io.IOException is thrown. // Don't retry. - if (SnowflakeUtil.getRootCause(ex) instanceof IOException) { + if (getRootCause(ex) instanceof IOException) { SnowflakeFileTransferAgent.throwNoSpaceLeftError(session, operation, ex, queryId); } @@ -912,7 +916,7 @@ private static void handleS3Exception( } } else { if (ex instanceof InterruptedException - || SnowflakeUtil.getRootCause(ex) instanceof SocketTimeoutException) { + || getRootCause(ex) instanceof SocketTimeoutException) { if (retryCount > s3Client.getMaxRetries()) { throw new SnowflakeSQLLoggedException( queryId, diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeUtilTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeUtilTest.java index 6e61d82dc..703e55b7c 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeUtilTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeUtilTest.java @@ -3,6 +3,8 @@ */ package net.snowflake.client.jdbc; +import static net.snowflake.client.jdbc.SnowflakeUtil.createCaseInsensitiveMap; +import static net.snowflake.client.jdbc.SnowflakeUtil.extractColumnMetadata; import static net.snowflake.client.jdbc.SnowflakeUtil.getSnowflakeType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -16,8 +18,13 @@ import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; import net.snowflake.client.category.TestCategoryCore; import net.snowflake.client.core.ObjectMapperFactory; +import org.apache.http.Header; +import org.apache.http.message.BasicHeader; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -39,8 +46,7 @@ public void testCreateMetadata() throws Throwable { SnowflakeColumnMetadata expectedColumnMetadata = createExpectedMetadata(rootNode, fieldOne, fieldTwo); // when - SnowflakeColumnMetadata columnMetadata = - SnowflakeUtil.extractColumnMetadata(rootNode, false, null); + SnowflakeColumnMetadata columnMetadata = extractColumnMetadata(rootNode, false, null); // then assertNotNull(columnMetadata); assertEquals( @@ -62,8 +68,7 @@ public void testCreateFieldsMetadataForObject() throws Throwable { rootNode.putIfAbsent("fields", fields); // when - SnowflakeColumnMetadata columnMetadata = - SnowflakeUtil.extractColumnMetadata(rootNode, false, null); + SnowflakeColumnMetadata columnMetadata = extractColumnMetadata(rootNode, false, null); // then assertNotNull(columnMetadata); assertEquals("OBJECT", columnMetadata.getTypeName()); @@ -82,6 +87,36 @@ public void testCreateFieldsMetadataForObject() throws Throwable { assertTrue(secondField.isNullable()); } + @Test + public void shouldConvertCreateCaseInsensitiveMap() { + Map map = new HashMap<>(); + map.put("key1", "value1"); + + map = SnowflakeUtil.createCaseInsensitiveMap(map); + assertTrue(map instanceof TreeMap); + assertEquals(String.CASE_INSENSITIVE_ORDER, ((TreeMap) map).comparator()); + assertEquals("value1", map.get("key1")); + assertEquals("value1", map.get("Key1")); + assertEquals("value1", map.get("KEy1")); + + map.put("KEY1", "changed_value1"); + assertEquals("changed_value1", map.get("KEY1")); + } + + @Test + public void shouldConvertHeadersCreateCaseInsensitiveMap() { + Header[] headers = + new Header[] {new BasicHeader("key1", "value1"), new BasicHeader("key2", "value2")}; + + Map map = createCaseInsensitiveMap(headers); + assertTrue(map instanceof TreeMap); + assertEquals(String.CASE_INSENSITIVE_ORDER, ((TreeMap) map).comparator()); + assertEquals("value1", map.get("key1")); + assertEquals("value2", map.get("key2")); + assertEquals("value1", map.get("Key1")); + assertEquals("value2", map.get("Key2")); + } + private static SnowflakeColumnMetadata createExpectedMetadata( JsonNode rootNode, JsonNode fieldOne, JsonNode fieldTwo) throws SnowflakeSQLLoggedException { ColumnTypeInfo columnTypeInfo = From babe0d8cbcaaeb5d77186422baf5256745ee372e Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:35:32 +0200 Subject: [PATCH 18/53] SNOW-799391: Add troubleshooting guide link for ssl exceptions (#1924) --- .../snowflake/client/jdbc/RestRequest.java | 9 +++- .../client/jdbc/ConnectionLatestIT.java | 36 ++++++++++++++ .../client/jdbc/ConnectionWithOCSPModeIT.java | 49 +++++++++++++------ 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/snowflake/client/jdbc/RestRequest.java b/src/main/java/net/snowflake/client/jdbc/RestRequest.java index 5be46c5de..c753c87de 100644 --- a/src/main/java/net/snowflake/client/jdbc/RestRequest.java +++ b/src/main/java/net/snowflake/client/jdbc/RestRequest.java @@ -283,7 +283,14 @@ public static CloseableHttpResponse execute( // if an SSL issue occurs like an SSLHandshakeException then fail // immediately and stop retrying the requests - throw new SnowflakeSQLLoggedException(null, ErrorCode.NETWORK_ERROR, ex, ex.getMessage()); + String formattedMsg = + ex.getMessage() + + "\n" + + "Verify that the hostnames and portnumbers in SYSTEM$ALLOWLIST are added to your firewall's allowed list.\n" + + "To troubleshoot your connection further, you can refer to this article:\n" + + "https://docs.snowflake.com/en/user-guide/client-connectivity-troubleshooting/overview"; + + throw new SnowflakeSQLLoggedException(null, ErrorCode.NETWORK_ERROR, ex, formattedMsg); } catch (Exception ex) { diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index efed33896..4d2129a53 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -12,6 +12,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.core.AnyOf.anyOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -48,7 +49,9 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Random; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLHandshakeException; import net.snowflake.client.ConditionalIgnoreRule; import net.snowflake.client.RunningNotOnAWS; import net.snowflake.client.RunningOnGithubAction; @@ -1618,4 +1621,37 @@ public void shouldGetOverridenConnectionAndSocketTimeouts() throws Exception { assertEquals(Duration.ofMillis(200), HttpUtil.getSocketTimeout()); } } + + /** Added in > 3.19.0 */ + @Test + public void shouldFailOnSslExceptionWithLinkToTroubleShootingGuide() throws InterruptedException { + Properties properties = new Properties(); + properties.put("user", "fakeuser"); + properties.put("password", "testpassword"); + properties.put("ocspFailOpen", Boolean.FALSE.toString()); + + int maxRetries = 5; + int retry = 0; + + // *.badssl.com may fail on timeouts + while (retry < maxRetries) { + try { + DriverManager.getConnection("jdbc:snowflake://expired.badssl.com/", properties); + fail("should fail"); + } catch (SQLException e) { + if (!(e.getCause() instanceof SSLHandshakeException)) { + retry++; + Thread.sleep(1000 * new Random().nextInt(3)); + continue; + } + assertThat(e.getCause(), instanceOf(SSLHandshakeException.class)); + assertTrue( + e.getMessage() + .contains( + "https://docs.snowflake.com/en/user-guide/client-connectivity-troubleshooting/overview")); + return; + } + } + fail("All retries failed"); + } } diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java index 04c9c9311..025d4c7a4 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java @@ -17,6 +17,7 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; +import java.util.Random; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; import net.snowflake.client.ConditionalIgnoreRule; @@ -412,22 +413,38 @@ public void testExpiredCert() { /** Test Wrong host. Will fail in both FAIL_OPEN and FAIL_CLOSED. */ @Test - public void testWrongHost() { - try { - DriverManager.getConnection( - "jdbc:snowflake://wrong.host.badssl.com/", OCSPFailClosedProperties()); - fail("should fail"); - } catch (SQLException ex) { - assertThat(ex, instanceOf(SnowflakeSQLException.class)); - - // The certificates used by badssl.com expired around 05/17/2022, - // https://github.com/chromium/badssl.com/issues/504. After the certificates had been updated, - // the exception seems to be changed from SSLPeerUnverifiedException to SSLHandshakeException. - assertThat( - ex.getCause(), - anyOf( - instanceOf(SSLPeerUnverifiedException.class), - instanceOf(SSLHandshakeException.class))); + public void testWrongHost() throws InterruptedException { + int maxRetries = 5; + int retry = 0; + + // *.badssl.com may fail on timeouts + while (retry < maxRetries) { + try { + DriverManager.getConnection( + "jdbc:snowflake://wrong.host.badssl.com/", OCSPFailClosedProperties()); + fail("should fail"); + } catch (SQLException ex) { + if (!(ex.getCause() instanceof SSLPeerUnverifiedException) + && !(ex.getCause() instanceof SSLHandshakeException)) { + retry++; + Thread.sleep(1000 * new Random().nextInt(3)); + continue; + } + assertThat(ex, instanceOf(SnowflakeSQLException.class)); + + // The certificates used by badssl.com expired around 05/17/2022, + // https://github.com/chromium/badssl.com/issues/504. After the certificates had been + // updated, + // the exception seems to be changed from SSLPeerUnverifiedException to + // SSLHandshakeException. + assertThat( + ex.getCause(), + anyOf( + instanceOf(SSLPeerUnverifiedException.class), + instanceOf(SSLHandshakeException.class))); + return; + } + fail("All retries failed"); } } From e5beff8d280a40d7ee3a854f2abe67bc108d17e6 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Tue, 22 Oct 2024 07:00:13 +0200 Subject: [PATCH 19/53] SNOW-1752941: Fix SFLoginOutput connection timeout (#1930) --- src/main/java/net/snowflake/client/core/SFLoginOutput.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/snowflake/client/core/SFLoginOutput.java b/src/main/java/net/snowflake/client/core/SFLoginOutput.java index 8daf81f10..3470076b9 100644 --- a/src/main/java/net/snowflake/client/core/SFLoginOutput.java +++ b/src/main/java/net/snowflake/client/core/SFLoginOutput.java @@ -18,6 +18,7 @@ public class SFLoginOutput { private int databaseMajorVersion; private int databaseMinorVersion; private Duration httpClientSocketTimeout; + private Duration httpClientConnectionTimeout; private String sessionDatabase; private String sessionSchema; private String sessionRole; @@ -53,6 +54,7 @@ public class SFLoginOutput { this.databaseMajorVersion = databaseMajorVersion; this.databaseMinorVersion = databaseMinorVersion; this.httpClientSocketTimeout = Duration.ofMillis(httpClientSocketTimeout); + this.httpClientConnectionTimeout = Duration.ofMillis(httpClientConnectionTimeout); this.sessionDatabase = sessionDatabase; this.sessionSchema = sessionSchema; this.sessionRole = sessionRole; @@ -113,7 +115,7 @@ Duration getHttpClientSocketTimeout() { } Duration getHttpClientConnectionTimeout() { - return httpClientSocketTimeout; + return httpClientConnectionTimeout; } Map getCommonParams() { From ea6abe14bf4fe2b821812c50acb7e788f61a320e Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:35:27 +0200 Subject: [PATCH 20/53] SNOW-1708304: Download stream from git repository (#1920) --- .../jdbc/SnowflakeFileTransferAgent.java | 27 ++++- .../client/jdbc/ConnectionLatestIT.java | 2 +- .../jdbc/GitRepositoryDownloadLatestIT.java | 99 +++++++++++++++++++ .../snowflake/client/jdbc/StreamLatestIT.java | 2 +- 4 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 src/test/java/net/snowflake/client/jdbc/GitRepositoryDownloadLatestIT.java diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java index bd5a3945e..895275cef 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java @@ -1649,6 +1649,7 @@ private void uploadStream() throws SnowflakeSQLException { /** Download a file from remote, and return an input stream */ @Override public InputStream downloadStream(String fileName) throws SnowflakeSQLException { + logger.debug("Downloading file as stream: {}", fileName); if (stageInfo.getStageType() == StageInfo.StageType.LOCAL_FS) { logger.error("downloadStream function doesn't support local file system", false); @@ -1662,14 +1663,32 @@ public InputStream downloadStream(String fileName) throws SnowflakeSQLException remoteLocation remoteLocation = extractLocationAndPath(stageInfo.getLocation()); - String stageFilePath = fileName; + // when downloading files as stream there should be only one file in source files + String sourceLocation = + sourceFiles.stream() + .findFirst() + .orElseThrow( + () -> + new SnowflakeSQLException( + queryID, + SqlState.NO_DATA, + ErrorCode.FILE_NOT_FOUND.getMessageCode(), + session, + "File not found: " + fileName)); + + if (!fileName.equals(sourceLocation)) { + // filename may be different from source location e.g. in git repositories + logger.debug("Changing file to download location from {} to {}", fileName, sourceLocation); + } + String stageFilePath = sourceLocation; if (!remoteLocation.path.isEmpty()) { - stageFilePath = SnowflakeUtil.concatFilePathNames(remoteLocation.path, fileName, "/"); + stageFilePath = SnowflakeUtil.concatFilePathNames(remoteLocation.path, sourceLocation, "/"); } + logger.debug("Stage file path for {} is {}", sourceLocation, stageFilePath); - RemoteStoreFileEncryptionMaterial encMat = srcFileToEncMat.get(fileName); - String presignedUrl = srcFileToPresignedUrl.get(fileName); + RemoteStoreFileEncryptionMaterial encMat = srcFileToEncMat.get(sourceLocation); + String presignedUrl = srcFileToPresignedUrl.get(sourceLocation); return storageFactory .createClient(stageInfo, parallel, encMat, session) diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index 4d2129a53..99ba7abdc 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -1316,7 +1316,7 @@ public void testDownloadStreamWithFileNotFoundException() throws SQLException { .unwrap(SnowflakeConnection.class) .downloadStream("@testDownloadStream_stage", "/fileNotExist.gz", true); } catch (SQLException ex) { - assertThat(ex.getErrorCode(), is(ErrorCode.S3_OPERATION_ERROR.getMessageCode())); + assertThat(ex.getErrorCode(), is(ErrorCode.FILE_NOT_FOUND.getMessageCode())); } long endDownloadTime = System.currentTimeMillis(); // S3Client retries some exception for a default timeout of 5 minutes diff --git a/src/test/java/net/snowflake/client/jdbc/GitRepositoryDownloadLatestIT.java b/src/test/java/net/snowflake/client/jdbc/GitRepositoryDownloadLatestIT.java new file mode 100644 index 000000000..b720591de --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/GitRepositoryDownloadLatestIT.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.jdbc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; +import net.snowflake.client.ConditionalIgnoreRule; +import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryOthers; +import org.apache.commons.io.IOUtils; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category(TestCategoryOthers.class) +public class GitRepositoryDownloadLatestIT extends BaseJDBCTest { + + /** + * Test needs to set up git integration which is not available in GH Action tests and needs + * accountadmin role. Added in > 3.19.0 + */ + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void shouldDownloadFileAndStreamFromGitRepository() throws Exception { + try (Connection connection = getConnection()) { + prepareJdbcRepoInSnowflake(connection); + + String stageName = + String.format("@%s.%s.JDBC", connection.getCatalog(), connection.getSchema()); + String fileName = ".pre-commit-config.yaml"; + String filePathInGitRepo = "branches/master/" + fileName; + + List fetchedFileContent = + getContentFromFile(connection, stageName, filePathInGitRepo, fileName); + + List fetchedStreamContent = + getContentFromStream(connection, stageName, filePathInGitRepo); + + assertFalse("File content cannot be empty", fetchedFileContent.isEmpty()); + assertFalse("Stream content cannot be empty", fetchedStreamContent.isEmpty()); + assertEquals(fetchedFileContent, fetchedStreamContent); + } + } + + private static void prepareJdbcRepoInSnowflake(Connection connection) throws SQLException { + try (Statement statement = connection.createStatement()) { + statement.execute("use role accountadmin"); + statement.execute( + "CREATE OR REPLACE API INTEGRATION gh_integration\n" + + " API_PROVIDER = git_https_api\n" + + " API_ALLOWED_PREFIXES = ('https://github.com/snowflakedb/snowflake-jdbc.git')\n" + + " ENABLED = TRUE;"); + statement.execute( + "CREATE OR REPLACE GIT REPOSITORY jdbc\n" + + "ORIGIN = 'https://github.com/snowflakedb/snowflake-jdbc.git'\n" + + "API_INTEGRATION = gh_integration;"); + } + } + + private static List getContentFromFile( + Connection connection, String stageName, String filePathInGitRepo, String fileName) + throws IOException, SQLException { + Path tempDir = Files.createTempDirectory("git"); + String stagePath = stageName + "/" + filePathInGitRepo; + Path downloadedFile = tempDir.resolve(fileName); + String command = String.format("GET '%s' '%s'", stagePath, tempDir.toUri()); + + try (Statement statement = connection.createStatement(); + ResultSet rs = statement.executeQuery(command); ) { + // then + assertTrue("has result", rs.next()); + return Files.readAllLines(downloadedFile); + } finally { + Files.delete(downloadedFile); + Files.delete(tempDir); + } + } + + private static List getContentFromStream( + Connection connection, String stageName, String filePathInGitRepo) + throws SQLException, IOException { + SnowflakeConnection unwrap = connection.unwrap(SnowflakeConnection.class); + try (InputStream inputStream = unwrap.downloadStream(stageName, filePathInGitRepo, false)) { + return IOUtils.readLines(inputStream, StandardCharsets.UTF_8); + } + } +} diff --git a/src/test/java/net/snowflake/client/jdbc/StreamLatestIT.java b/src/test/java/net/snowflake/client/jdbc/StreamLatestIT.java index 3ab179b70..093c2de27 100644 --- a/src/test/java/net/snowflake/client/jdbc/StreamLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StreamLatestIT.java @@ -119,7 +119,7 @@ public void testDownloadToStreamBlobNotFoundGCS() throws SQLException { assertTrue(ex instanceof SQLException); assertTrue( "Wrong exception message: " + ex.getMessage(), - ex.getMessage().matches(".*Blob.*not found in bucket.*")); + ex.getMessage().contains("File not found")); } finally { statement.execute("rm @~/" + DEST_PREFIX); } From b1d7d9deecbb5016186cc2ca2c7c9952dfd75adf Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:26:22 +0200 Subject: [PATCH 21/53] SNOW-1759523: Accept 404 for OCSP tests with fakeaccount (#1935) --- .../client/jdbc/ConnectionWithOCSPModeIT.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java index 025d4c7a4..eee8466df 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java @@ -110,7 +110,7 @@ public void testValidityExpiredOCSPResponseFailOpen() { } catch (SQLException ex) { assertThat(ex, instanceOf(SnowflakeSQLException.class)); assertThat(ex.getErrorCode(), equalTo(NETWORK_ERROR.getMessageCode())); - assertThat(ex.getMessage(), httpStatus403Or513()); + assertThat(ex.getMessage(), httpStatus403Or404Or513()); assertNull(ex.getCause()); } } @@ -148,7 +148,7 @@ public void testNoOCSPResponderURLFailOpen() { } catch (SQLException ex) { assertThat(ex, instanceOf(SnowflakeSQLException.class)); assertThat(ex.getErrorCode(), equalTo(NETWORK_ERROR.getMessageCode())); - assertThat(ex.getMessage(), httpStatus403Or513()); + assertThat(ex.getMessage(), httpStatus403Or404Or513()); assertNull(ex.getCause()); } } @@ -185,7 +185,7 @@ public void testValidityExpiredOCSPResponseInsecure() { } catch (SQLException ex) { assertThat(ex, instanceOf(SnowflakeSQLException.class)); assertThat(ex.getErrorCode(), equalTo(NETWORK_ERROR.getMessageCode())); - assertThat(ex.getMessage(), httpStatus403Or513()); + assertThat(ex.getMessage(), httpStatus403Or404Or513()); assertNull(ex.getCause()); } } @@ -200,7 +200,7 @@ public void testCertAttachedInvalidFailOpen() { } catch (SQLException ex) { assertThat(ex, instanceOf(SnowflakeSQLException.class)); assertThat(ex.getErrorCode(), equalTo(NETWORK_ERROR.getMessageCode())); - assertThat(ex.getMessage(), httpStatus403Or513()); + assertThat(ex.getMessage(), httpStatus403Or404Or513()); assertNull(ex.getCause()); } } @@ -236,7 +236,7 @@ public void testUnknownOCSPCertFailOpen() { } catch (SQLException ex) { assertThat(ex, instanceOf(SnowflakeSQLException.class)); assertThat(ex.getErrorCode(), equalTo(NETWORK_ERROR.getMessageCode())); - assertThat(ex.getMessage(), httpStatus403Or513()); + assertThat(ex.getMessage(), httpStatus403Or404Or513()); assertNull(ex.getCause()); } } @@ -295,7 +295,7 @@ public void testOCSPCacheServerTimeoutFailOpen() { } catch (SQLException ex) { assertThat(ex, instanceOf(SnowflakeSQLException.class)); assertThat(ex.getErrorCode(), equalTo(NETWORK_ERROR.getMessageCode())); - assertThat(ex.getMessage(), httpStatus403Or513()); + assertThat(ex.getMessage(), httpStatus403Or404Or513()); assertNull(ex.getCause()); } } @@ -334,7 +334,7 @@ public void testOCSPResponderTimeoutFailOpen() { } catch (SQLException ex) { assertThat(ex, instanceOf(SnowflakeSQLException.class)); assertThat(ex.getErrorCode(), equalTo(NETWORK_ERROR.getMessageCode())); - assertThat(ex.getMessage(), httpStatus403Or513()); + assertThat(ex.getMessage(), httpStatus403Or404Or513()); assertNull(ex.getCause()); } } @@ -370,7 +370,7 @@ public void testOCSPResponder403FailOpen() { } catch (SQLException ex) { assertThat(ex, instanceOf(SnowflakeSQLException.class)); assertThat(ex.getErrorCode(), equalTo(NETWORK_ERROR.getMessageCode())); - assertThat(ex.getMessage(), httpStatus403Or513()); + assertThat(ex.getMessage(), httpStatus403Or404Or513()); assertNull(ex.getCause()); } } @@ -448,7 +448,10 @@ public void testWrongHost() throws InterruptedException { } } - private static Matcher httpStatus403Or513() { - return anyOf(containsString("HTTP status=403"), containsString("HTTP status=513")); + private static Matcher httpStatus403Or404Or513() { + return anyOf( + containsString("HTTP status=403"), + containsString("HTTP status=404"), + containsString("HTTP status=513")); } } From e609432818fc9197d468fece54f0216e63a4fbeb Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:09:38 +0200 Subject: [PATCH 22/53] SNOW-799391: Ignore badssl timeouts in tests (#1931) --- .../client/jdbc/ConnectionLatestIT.java | 33 +++++------- .../client/jdbc/ConnectionWithOCSPModeIT.java | 53 ++++++++----------- 2 files changed, 35 insertions(+), 51 deletions(-) diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index 99ba7abdc..4dbbcb021 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -49,7 +49,6 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.Random; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLHandshakeException; import net.snowflake.client.ConditionalIgnoreRule; @@ -1630,28 +1629,20 @@ public void shouldFailOnSslExceptionWithLinkToTroubleShootingGuide() throws Inte properties.put("password", "testpassword"); properties.put("ocspFailOpen", Boolean.FALSE.toString()); - int maxRetries = 5; - int retry = 0; - - // *.badssl.com may fail on timeouts - while (retry < maxRetries) { - try { - DriverManager.getConnection("jdbc:snowflake://expired.badssl.com/", properties); - fail("should fail"); - } catch (SQLException e) { - if (!(e.getCause() instanceof SSLHandshakeException)) { - retry++; - Thread.sleep(1000 * new Random().nextInt(3)); - continue; - } - assertThat(e.getCause(), instanceOf(SSLHandshakeException.class)); - assertTrue( - e.getMessage() - .contains( - "https://docs.snowflake.com/en/user-guide/client-connectivity-troubleshooting/overview")); + try { + DriverManager.getConnection("jdbc:snowflake://expired.badssl.com/", properties); + fail("should fail"); + } catch (SQLException e) { + // *.badssl.com may fail with timeout + if (!(e.getCause() instanceof SSLHandshakeException) + && e.getCause().getMessage().toLowerCase().contains("timed out")) { return; } + assertThat(e.getCause(), instanceOf(SSLHandshakeException.class)); + assertTrue( + e.getMessage() + .contains( + "https://docs.snowflake.com/en/user-guide/client-connectivity-troubleshooting/overview")); } - fail("All retries failed"); } } diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java index eee8466df..49c6c6d10 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java @@ -17,7 +17,6 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; -import java.util.Random; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; import net.snowflake.client.ConditionalIgnoreRule; @@ -414,38 +413,32 @@ public void testExpiredCert() { /** Test Wrong host. Will fail in both FAIL_OPEN and FAIL_CLOSED. */ @Test public void testWrongHost() throws InterruptedException { - int maxRetries = 5; - int retry = 0; - - // *.badssl.com may fail on timeouts - while (retry < maxRetries) { - try { - DriverManager.getConnection( - "jdbc:snowflake://wrong.host.badssl.com/", OCSPFailClosedProperties()); - fail("should fail"); - } catch (SQLException ex) { - if (!(ex.getCause() instanceof SSLPeerUnverifiedException) - && !(ex.getCause() instanceof SSLHandshakeException)) { - retry++; - Thread.sleep(1000 * new Random().nextInt(3)); - continue; - } - assertThat(ex, instanceOf(SnowflakeSQLException.class)); - - // The certificates used by badssl.com expired around 05/17/2022, - // https://github.com/chromium/badssl.com/issues/504. After the certificates had been - // updated, - // the exception seems to be changed from SSLPeerUnverifiedException to - // SSLHandshakeException. - assertThat( - ex.getCause(), - anyOf( - instanceOf(SSLPeerUnverifiedException.class), - instanceOf(SSLHandshakeException.class))); + try { + DriverManager.getConnection( + "jdbc:snowflake://wrong.host.badssl.com/", OCSPFailClosedProperties()); + fail("should fail"); + } catch (SQLException ex) { + // *.badssl.com may fail with timeout + if (!(ex.getCause() instanceof SSLPeerUnverifiedException) + && !(ex.getCause() instanceof SSLHandshakeException) + && ex.getCause().getMessage().toLowerCase().contains("timed out")) { return; } - fail("All retries failed"); + assertThat(ex, instanceOf(SnowflakeSQLException.class)); + + // The certificates used by badssl.com expired around 05/17/2022, + // https://github.com/chromium/badssl.com/issues/504. After the certificates had been + // updated, + // the exception seems to be changed from SSLPeerUnverifiedException to + // SSLHandshakeException. + assertThat( + ex.getCause(), + anyOf( + instanceOf(SSLPeerUnverifiedException.class), + instanceOf(SSLHandshakeException.class))); + return; } + fail("All retries failed"); } private static Matcher httpStatus403Or404Or513() { From 323fb5469270af5b4a1a6971a208da2f7ed06ebd Mon Sep 17 00:00:00 2001 From: Jelena Furundzic <141762304+sfc-gh-ext-simba-jf@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:39:13 -0700 Subject: [PATCH 23/53] SNOW-1213120: Reuse connections in tests 3 (#1819) Co-authored-by: Piotr Bulawa --- .../jdbc/DatabaseMetaDataResultsetIT.java | 6 +- .../client/jdbc/MultiStatementLatestIT.java | 32 ++- .../client/jdbc/OpenGroupCLIFuncIT.java | 188 ++++++++---------- .../client/jdbc/PreparedMultiStmtArrowIT.java | 12 -- .../client/jdbc/PreparedMultiStmtIT.java | 53 +++-- 5 files changed, 133 insertions(+), 158 deletions(-) delete mode 100644 src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtArrowIT.java diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultsetIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultsetIT.java index febcd4501..ccc984e3c 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultsetIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultsetIT.java @@ -8,7 +8,6 @@ import static org.junit.Assert.assertTrue; import java.math.BigDecimal; -import java.sql.Connection; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; @@ -23,7 +22,7 @@ import org.junit.experimental.categories.Category; @Category(TestCategoryOthers.class) -public class DatabaseMetaDataResultsetIT extends BaseJDBCTest { +public class DatabaseMetaDataResultsetIT extends BaseJDBCWithSharedConnectionIT { private static final int columnCount = 9; private static final int INT_DATA = 1; private static final String TEXT_DATA = "TEST"; @@ -98,8 +97,7 @@ public void testRowIndex() throws SQLException { } private ResultSet getResultSet(boolean doNext) throws SQLException { - Connection con = getConnection(); - Statement st = con.createStatement(); + Statement st = connection.createStatement(); ResultSet resultSet = new SnowflakeDatabaseMetaDataResultSet(columnNames, columnTypeNames, columnTypes, rows, st); if (doNext) { diff --git a/src/test/java/net/snowflake/client/jdbc/MultiStatementLatestIT.java b/src/test/java/net/snowflake/client/jdbc/MultiStatementLatestIT.java index 59f5ba795..eedf56114 100644 --- a/src/test/java/net/snowflake/client/jdbc/MultiStatementLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/MultiStatementLatestIT.java @@ -9,11 +9,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import net.snowflake.client.category.TestCategoryStatement; +import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -24,21 +24,19 @@ * both the latest and oldest supported driver run the tests. */ @Category(TestCategoryStatement.class) -public class MultiStatementLatestIT extends BaseJDBCTest { +public class MultiStatementLatestIT extends BaseJDBCWithSharedConnectionIT { protected static String queryResultFormat = "json"; - public static Connection getConnection() throws SQLException { - Connection conn = BaseJDBCTest.getConnection(); - try (Statement stmt = conn.createStatement()) { + @Before + public void setQueryResultFormat() throws SQLException { + try (Statement stmt = connection.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); } - return conn; } @Test public void testMultiStmtExecute() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { statement.unwrap(SnowflakeStatement.class).setParameter("MULTI_STATEMENT_COUNT", 3); String multiStmtQuery = "create or replace temporary table test_multi (cola int);\n" @@ -74,8 +72,7 @@ public void testMultiStmtExecute() throws SQLException { @Test public void testMultiStmtTransaction() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { try { statement.execute( "create or replace table test_multi_txn(c1 number, c2 string)" + " as select 10, 'z'"); @@ -120,8 +117,7 @@ public void testMultiStmtTransaction() throws SQLException { @Test public void testMultiStmtExecuteUpdate() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String multiStmtQuery = "create or replace temporary table test_multi (cola int);\n" + "insert into test_multi VALUES (1), (2);\n" @@ -157,8 +153,7 @@ public void testMultiStmtExecuteUpdate() throws SQLException { @Test public void testMultiStmtTransactionRollback() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { try { statement.execute( "create or replace table test_multi_txn_rb(c1 number, c2 string)" @@ -207,8 +202,7 @@ public void testMultiStmtTransactionRollback() throws SQLException { @Test public void testMultiStmtExecuteQuery() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String multiStmtQuery = "select 1;\n" + "create or replace temporary table test_multi (cola int);\n" @@ -254,8 +248,7 @@ public void testMultiStmtExecuteQuery() throws SQLException { @Test public void testMultiStmtUpdateCount() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { statement.unwrap(SnowflakeStatement.class).setParameter("MULTI_STATEMENT_COUNT", 2); boolean isResultSet = statement.execute( @@ -280,8 +273,7 @@ public void testMultiStmtUpdateCount() throws SQLException { /** Test use of anonymous blocks (SNOW-758262) */ @Test public void testAnonymousBlocksUse() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { statement.execute("create or replace table tab758262(c1 number)"); // Test anonymous block with multistatement int multistatementcount = 2; diff --git a/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncIT.java b/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncIT.java index d767456a2..d68dc8fc5 100644 --- a/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncIT.java +++ b/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncIT.java @@ -10,17 +10,18 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import net.snowflake.client.AbstractDriverIT; import net.snowflake.client.TestUtil; import net.snowflake.client.category.TestCategoryOthers; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; /** Test OpenGroup CLI */ @Category(TestCategoryOthers.class) -public class OpenGroupCLIFuncIT extends BaseJDBCTest { - public static Connection getConnection() throws SQLException { - Connection connection = AbstractDriverIT.getConnection(); +public class OpenGroupCLIFuncIT extends BaseJDBCWithSharedConnectionIT { + + @BeforeClass + public static void setSessionTimezone() throws SQLException { try (Statement statement = connection.createStatement()) { statement.execute( "alter session set " @@ -31,114 +32,101 @@ public static Connection getConnection() throws SQLException { + "TIMESTAMP_LTZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'," + "TIMESTAMP_NTZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'"); } - return connection; } @Test public void testStringFunction() throws SQLException { - try (Connection connection = getConnection()) { - testFunction(connection, "select {fn ASCII('snowflake')}", "115"); - testFunction(connection, "select {fn CHAR(115)}", "s"); - testFunction(connection, "select {fn CONCAT('snow', 'flake')}", "snowflake"); - // DIFFERENCE is not supported - // testFunction(connection, "select {fn DIFFERENCE('snow', 'flake')}", "snowflake"); - testFunction(connection, "select {fn INSERT('snowflake', 2, 3, 'insert')}", "sinsertflake"); - testFunction(connection, "select {fn LCASE('SNOWflake')}", "snowflake"); - testFunction(connection, "select {fn LEFT('snowflake', 4)}", "snow"); - testFunction(connection, "select {fn LENGTH(' snowflake ')}", "11"); - testFunction(connection, "select {fn LOCATE('str', 'strstrstr', 2)}", "4"); - testFunction(connection, "select {fn LTRIM(' snowflake ')}", "snowflake "); - testFunction(connection, "select {fn REPEAT('snow', 3)}", "snowsnowsnow"); - testFunction(connection, "select {fn REPLACE('snowssnowsn', 'sn', 'aa')}", "aaowsaaowaa"); - testFunction(connection, "select {fn RIGHT('snowflake', 5)}", "flake"); - testFunction(connection, "select {fn RTRIM(' snowflake ')}", " snowflake"); - // SOUNDEX is not supported - // testFunction(connection, "select {fn SOUNDEX('snowflake')}", " snowflake"); - testFunction(connection, "select {fn SPACE(4)}", " "); - testFunction(connection, "select {fn SUBSTRING('snowflake', 2, 3)}", "now"); - testFunction(connection, "select {fn UCASE('snowflake')}", "SNOWFLAKE"); - } + testFunction(connection, "select {fn ASCII('snowflake')}", "115"); + testFunction(connection, "select {fn CHAR(115)}", "s"); + testFunction(connection, "select {fn CONCAT('snow', 'flake')}", "snowflake"); + // DIFFERENCE is not supported + // testFunction(connection, "select {fn DIFFERENCE('snow', 'flake')}", "snowflake"); + testFunction(connection, "select {fn INSERT('snowflake', 2, 3, 'insert')}", "sinsertflake"); + testFunction(connection, "select {fn LCASE('SNOWflake')}", "snowflake"); + testFunction(connection, "select {fn LEFT('snowflake', 4)}", "snow"); + testFunction(connection, "select {fn LENGTH(' snowflake ')}", "11"); + testFunction(connection, "select {fn LOCATE('str', 'strstrstr', 2)}", "4"); + testFunction(connection, "select {fn LTRIM(' snowflake ')}", "snowflake "); + testFunction(connection, "select {fn REPEAT('snow', 3)}", "snowsnowsnow"); + testFunction(connection, "select {fn REPLACE('snowssnowsn', 'sn', 'aa')}", "aaowsaaowaa"); + testFunction(connection, "select {fn RIGHT('snowflake', 5)}", "flake"); + testFunction(connection, "select {fn RTRIM(' snowflake ')}", " snowflake"); + // SOUNDEX is not supported + // testFunction(connection, "select {fn SOUNDEX('snowflake')}", " snowflake"); + testFunction(connection, "select {fn SPACE(4)}", " "); + testFunction(connection, "select {fn SUBSTRING('snowflake', 2, 3)}", "now"); + testFunction(connection, "select {fn UCASE('snowflake')}", "SNOWFLAKE"); } @Test public void testDateTimeFunction() throws SQLException { - try (Connection connection = getConnection()) { - // testFunction(connection, "select {fn CURDATE()}",""); - // testFunction(connection, "select {fn CURTIME()}",""); - testFunction(connection, "select {fn DAYNAME('2016-5-25')}", "Wed"); - testFunction(connection, "select {fn DAYOFMONTH(to_date('2016-5-25'))}", "25"); - testFunction(connection, "select {fn DAYOFWEEK(to_date('2016-5-25'))}", "3"); - testFunction(connection, "select {fn DAYOFYEAR(to_date('2016-5-25'))}", "146"); - testFunction(connection, "select {fn HOUR(to_timestamp('2016-5-25 12:34:56.789789'))}", "12"); - testFunction( - connection, "select {fn MINUTE(to_timestamp('2016-5-25 12:34:56.789789'))}", "34"); - testFunction(connection, "select {fn MONTH(to_date('2016-5-25'))}", "5"); - testFunction(connection, "select {fn MONTHNAME(to_date('2016-5-25'))}", "May"); - // testFunction(connection, "select {fn NOW()}", "May"); - testFunction(connection, "select {fn QUARTER(to_date('2016-5-25'))}", "2"); - testFunction( - connection, "select {fn SECOND(to_timestamp('2016-5-25 12:34:56.789789'))}", "56"); - testFunction( - connection, - "select {fn TIMESTAMPADD(SQL_TSI_FRAC_SECOND, 1000, " - + "to_timestamp('2016-5-25 12:34:56.789789'))}", - "Wed, 25 May 2016 12:34:56 -0700"); - testFunction( - connection, - "select {fn TIMESTAMPADD(SQL_TSI_SECOND, 1, " - + "to_timestamp('2016-5-25 12:34:56.789789'))}", - "Wed, 25 May 2016 12:34:57 -0700"); - testFunction( - connection, - "select {fn TIMESTAMPADD(SQL_TSI_MINUTE, 1, " - + "to_timestamp('2016-5-25 12:34:56.789789'))}", - "Wed, 25 May 2016 12:35:56 -0700"); - testFunction( - connection, - "select {fn TIMESTAMPADD(SQL_TSI_HOUR, 1, " - + "to_timestamp('2016-5-25 12:34:56.789789'))}", - "Wed, 25 May 2016 13:34:56 -0700"); - testFunction( - connection, - "select {fn TIMESTAMPADD(SQL_TSI_DAY, 1, " - + "to_timestamp('2016-5-25 12:34:56.789789'))}", - "Thu, 26 May 2016 12:34:56 -0700"); - testFunction( - connection, - "select {fn TIMESTAMPADD(SQL_TSI_MONTH, 1, " - + "to_timestamp('2016-5-25 12:34:56.789789'))}", - "Sat, 25 Jun 2016 12:34:56 -0700"); - testFunction( - connection, - "select {fn TIMESTAMPADD(SQL_TSI_QUARTER, 1, " - + "to_timestamp('2016-5-25 12:34:56.789789'))}", - "Thu, 25 Aug 2016 12:34:56 -0700"); - testFunction( - connection, - "select {fn TIMESTAMPADD(SQL_TSI_YEAR, 1, " - + "to_timestamp('2016-5-25 12:34:56.789789'))}", - "Thu, 25 May 2017 12:34:56 -0700"); - testFunction( - connection, - "select {fn TIMESTAMPDIFF(SQL_TSI_SECOND, " - + "to_timestamp('2016-5-25 12:34:56.789789'), to_timestamp('2016-5-25 12:34:57.789789'))}", - "1"); - testFunction(connection, "select {fn WEEK(to_timestamp('2016-5-25 12:34:56.789789'))}", "21"); - testFunction( - connection, "select {fn YEAR(to_timestamp('2016-5-25 12:34:56.789789'))}", "2016"); - } + // testFunction(connection, "select {fn CURDATE()}",""); + // testFunction(connection, "select {fn CURTIME()}",""); + testFunction(connection, "select {fn DAYNAME('2016-5-25')}", "Wed"); + testFunction(connection, "select {fn DAYOFMONTH(to_date('2016-5-25'))}", "25"); + testFunction(connection, "select {fn DAYOFWEEK(to_date('2016-5-25'))}", "3"); + testFunction(connection, "select {fn DAYOFYEAR(to_date('2016-5-25'))}", "146"); + testFunction(connection, "select {fn HOUR(to_timestamp('2016-5-25 12:34:56.789789'))}", "12"); + testFunction(connection, "select {fn MINUTE(to_timestamp('2016-5-25 12:34:56.789789'))}", "34"); + testFunction(connection, "select {fn MONTH(to_date('2016-5-25'))}", "5"); + testFunction(connection, "select {fn MONTHNAME(to_date('2016-5-25'))}", "May"); + // testFunction(connection, "select {fn NOW()}", "May"); + testFunction(connection, "select {fn QUARTER(to_date('2016-5-25'))}", "2"); + testFunction(connection, "select {fn SECOND(to_timestamp('2016-5-25 12:34:56.789789'))}", "56"); + testFunction( + connection, + "select {fn TIMESTAMPADD(SQL_TSI_FRAC_SECOND, 1000, " + + "to_timestamp('2016-5-25 12:34:56.789789'))}", + "Wed, 25 May 2016 12:34:56 -0700"); + testFunction( + connection, + "select {fn TIMESTAMPADD(SQL_TSI_SECOND, 1, " + + "to_timestamp('2016-5-25 12:34:56.789789'))}", + "Wed, 25 May 2016 12:34:57 -0700"); + testFunction( + connection, + "select {fn TIMESTAMPADD(SQL_TSI_MINUTE, 1, " + + "to_timestamp('2016-5-25 12:34:56.789789'))}", + "Wed, 25 May 2016 12:35:56 -0700"); + testFunction( + connection, + "select {fn TIMESTAMPADD(SQL_TSI_HOUR, 1, " + "to_timestamp('2016-5-25 12:34:56.789789'))}", + "Wed, 25 May 2016 13:34:56 -0700"); + testFunction( + connection, + "select {fn TIMESTAMPADD(SQL_TSI_DAY, 1, " + "to_timestamp('2016-5-25 12:34:56.789789'))}", + "Thu, 26 May 2016 12:34:56 -0700"); + testFunction( + connection, + "select {fn TIMESTAMPADD(SQL_TSI_MONTH, 1, " + + "to_timestamp('2016-5-25 12:34:56.789789'))}", + "Sat, 25 Jun 2016 12:34:56 -0700"); + testFunction( + connection, + "select {fn TIMESTAMPADD(SQL_TSI_QUARTER, 1, " + + "to_timestamp('2016-5-25 12:34:56.789789'))}", + "Thu, 25 Aug 2016 12:34:56 -0700"); + testFunction( + connection, + "select {fn TIMESTAMPADD(SQL_TSI_YEAR, 1, " + "to_timestamp('2016-5-25 12:34:56.789789'))}", + "Thu, 25 May 2017 12:34:56 -0700"); + testFunction( + connection, + "select {fn TIMESTAMPDIFF(SQL_TSI_SECOND, " + + "to_timestamp('2016-5-25 12:34:56.789789'), to_timestamp('2016-5-25 12:34:57.789789'))}", + "1"); + testFunction(connection, "select {fn WEEK(to_timestamp('2016-5-25 12:34:56.789789'))}", "21"); + testFunction(connection, "select {fn YEAR(to_timestamp('2016-5-25 12:34:56.789789'))}", "2016"); } @Test public void testSystemFunctions() throws SQLException { - try (Connection connection = getConnection()) { - testFunction(connection, "select {fn DATABASE()}", connection.getCatalog()); - testFunction(connection, "select {fn IFNULL(NULL, 1)}", "1"); - testFunction( - connection, - "select {fn USER()}", - TestUtil.systemGetEnv("SNOWFLAKE_TEST_USER").toUpperCase()); - } + testFunction(connection, "select {fn DATABASE()}", connection.getCatalog()); + testFunction(connection, "select {fn IFNULL(NULL, 1)}", "1"); + testFunction( + connection, + "select {fn USER()}", + TestUtil.systemGetEnv("SNOWFLAKE_TEST_USER").toUpperCase()); } static void testFunction(Connection connection, String sql, String expected) throws SQLException { diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtArrowIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtArrowIT.java deleted file mode 100644 index 54a3e8ec1..000000000 --- a/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtArrowIT.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.snowflake.client.jdbc; - -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; - -@Category(TestCategoryArrow.class) -public class PreparedMultiStmtArrowIT extends PreparedMultiStmtIT { - public PreparedMultiStmtArrowIT() { - super(); - queryResultFormat = "arrow"; - } -} diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtIT.java index 3d1997193..224c538d0 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtIT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtIT.java @@ -5,39 +5,52 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; -import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import net.snowflake.client.category.TestCategoryStatement; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +@RunWith(Parameterized.class) @Category(TestCategoryStatement.class) -public class PreparedMultiStmtIT extends BaseJDBCTest { +public class PreparedMultiStmtIT extends BaseJDBCWithSharedConnectionIT { - protected static String queryResultFormat = "json"; + @Parameterized.Parameters(name = "format={0}") + public static Object[][] data() { + // all tests in this class need to run for both query result formats json and arrow + return new Object[][] {{"JSON"}, {"Arrow"}}; + } + + protected String queryResultFormat; + private static SnowflakeConnectionV1 sfConnectionV1; + + public PreparedMultiStmtIT(String queryResultFormat) { + this.queryResultFormat = queryResultFormat; + this.sfConnectionV1 = (SnowflakeConnectionV1) connection; + } - public static Connection getConnection() throws SQLException { - Connection conn = BaseJDBCTest.getConnection(); - try (Statement stmt = conn.createStatement()) { + @Before + public void setSessionResultFormat() throws SQLException { + try (Statement stmt = connection.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); } - return conn; } @Test public void testExecuteUpdateCount() throws Exception { - try (SnowflakeConnectionV1 connection = (SnowflakeConnectionV1) getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = sfConnectionV1.createStatement()) { try { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); statement.execute("create or replace table test_multi_bind(c1 number)"); try (PreparedStatement preparedStatement = - connection.prepareStatement( + sfConnectionV1.prepareStatement( "insert into test_multi_bind(c1) values(?); insert into " + "test_multi_bind values (?), (?)")) { @@ -76,14 +89,13 @@ public void testExecuteUpdateCount() throws Exception { /** Less bindings than expected in statement */ @Test public void testExecuteLessBindings() throws Exception { - try (SnowflakeConnectionV1 connection = (SnowflakeConnectionV1) getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = sfConnectionV1.createStatement()) { try { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); statement.execute("create or replace table test_multi_bind(c1 number)"); try (PreparedStatement preparedStatement = - connection.prepareStatement( + sfConnectionV1.prepareStatement( "insert into test_multi_bind(c1) values(?); insert into " + "test_multi_bind values (?), (?)")) { @@ -109,14 +121,13 @@ public void testExecuteLessBindings() throws Exception { @Test public void testExecuteMoreBindings() throws Exception { - try (SnowflakeConnectionV1 connection = (SnowflakeConnectionV1) getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = sfConnectionV1.createStatement()) { try { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); statement.execute("create or replace table test_multi_bind(c1 number)"); try (PreparedStatement preparedStatement = - connection.prepareStatement( + sfConnectionV1.prepareStatement( "insert into test_multi_bind(c1) values(?); insert into " + "test_multi_bind values (?), (?)")) { @@ -156,12 +167,11 @@ public void testExecuteMoreBindings() throws Exception { @Test public void testExecuteQueryBindings() throws Exception { - try (SnowflakeConnectionV1 connection = (SnowflakeConnectionV1) getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = sfConnectionV1.createStatement()) { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); try (PreparedStatement preparedStatement = - connection.prepareStatement("select ?; select ?, ?; select ?, ?, ?")) { + sfConnectionV1.prepareStatement("select ?; select ?, ?; select ?, ?, ?")) { assertThat(preparedStatement.getParameterMetaData().getParameterCount(), is(6)); @@ -199,12 +209,11 @@ public void testExecuteQueryBindings() throws Exception { @Test public void testExecuteQueryNoBindings() throws Exception { - try (SnowflakeConnectionV1 connection = (SnowflakeConnectionV1) getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = sfConnectionV1.createStatement()) { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); try (PreparedStatement preparedStatement = - connection.prepareStatement("select 10; select 20, 30; select 40, 50, 60")) { + sfConnectionV1.prepareStatement("select 10; select 20, 30; select 40, 50, 60")) { assertThat(preparedStatement.getParameterMetaData().getParameterCount(), is(0)); From 8bc1ae568de038a79e2405b11f469d913e0d73ae Mon Sep 17 00:00:00 2001 From: Jelena Furundzic <141762304+sfc-gh-ext-simba-jf@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:57:16 -0700 Subject: [PATCH 24/53] Snow-1213120: Reuse-Connections-4 (#1817) Co-authored-by: Piotr Bulawa --- .../jdbc/ClientMemoryLimitParallelIT.java | 11 +- .../snowflake/client/jdbc/ConnectionIT.java | 102 ++- .../client/jdbc/DatabaseMetaDataIT.java | 638 +++++++++--------- .../client/jdbc/DatabaseMetaDataLatestIT.java | 188 +++--- 4 files changed, 444 insertions(+), 495 deletions(-) diff --git a/src/test/java/net/snowflake/client/jdbc/ClientMemoryLimitParallelIT.java b/src/test/java/net/snowflake/client/jdbc/ClientMemoryLimitParallelIT.java index 22a33286d..56d954653 100644 --- a/src/test/java/net/snowflake/client/jdbc/ClientMemoryLimitParallelIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ClientMemoryLimitParallelIT.java @@ -21,7 +21,7 @@ * @author azhan attempts to test the CLIENT_MEMORY_LIMIT working in multi-threading */ @Category(TestCategoryOthers.class) -public class ClientMemoryLimitParallelIT { +public class ClientMemoryLimitParallelIT extends BaseJDBCWithSharedConnectionIT { private static Logger LOGGER = LoggerFactory.getLogger(ClientMemoryLimitParallelIT.class.getName()); @@ -64,16 +64,14 @@ public class ClientMemoryLimitParallelIT { @Before public void setUp() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { statement.execute(createTestTableSQL); } } @After public void tearDown() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { statement.execute("drop table if exists testtable_cml"); } } @@ -126,8 +124,7 @@ public void run() { @Test public void testQueryNotHanging() throws SQLException { Properties paramProperties = new Properties(); - try (Connection connection = getConnection(paramProperties); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { queryRows(statement, 100, 160); } } diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionIT.java index 43c5c7f81..00656e305 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionIT.java @@ -59,7 +59,7 @@ /** Connection integration tests */ @Category(TestCategoryConnection.class) -public class ConnectionIT extends BaseJDBCTest { +public class ConnectionIT extends BaseJDBCWithSharedConnectionIT { // create a local constant for this code for testing purposes (already defined in GS) public static final int INVALID_CONNECTION_INFO_CODE = 390100; private static final int SESSION_CREATION_OBJECT_DOES_NOT_EXIST_NOT_AUTHORIZED = 390201; @@ -90,10 +90,9 @@ public void testSimpleConnection() throws SQLException { public void test300ConnectionsWithSingleClientInstance() throws SQLException { // concurrent testing int size = 300; - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - String database = con.getCatalog(); - String schema = con.getSchema(); + try (Statement statement = connection.createStatement()) { + String database = connection.getCatalog(); + String schema = connection.getSchema(); statement.execute( "create or replace table bigTable(rowNum number,rando " + "number) as (select seq4()," @@ -168,8 +167,7 @@ public void testProdConnectivity() throws SQLException { @Test public void testSetCatalogSchema() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String db = connection.getCatalog(); String schema = connection.getSchema(); connection.setCatalog(db); @@ -220,31 +218,30 @@ public void testDataCompletenessInLowMemory() throws Exception { public void testConnectionGetAndSetDBAndSchema() throws SQLException { final String SECOND_DATABASE = "SECOND_DATABASE"; final String SECOND_SCHEMA = "SECOND_SCHEMA"; - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { try { final String database = TestUtil.systemGetEnv("SNOWFLAKE_TEST_DATABASE").toUpperCase(); final String schema = TestUtil.systemGetEnv("SNOWFLAKE_TEST_SCHEMA").toUpperCase(); - assertEquals(database, con.getCatalog()); - assertEquals(schema, con.getSchema()); + assertEquals(database, connection.getCatalog()); + assertEquals(schema, connection.getSchema()); statement.execute(String.format("create or replace database %s", SECOND_DATABASE)); statement.execute(String.format("create or replace schema %s", SECOND_SCHEMA)); statement.execute(String.format("use database %s", database)); - con.setCatalog(SECOND_DATABASE); - assertEquals(SECOND_DATABASE, con.getCatalog()); - assertEquals("PUBLIC", con.getSchema()); + connection.setCatalog(SECOND_DATABASE); + assertEquals(SECOND_DATABASE, connection.getCatalog()); + assertEquals("PUBLIC", connection.getSchema()); - con.setSchema(SECOND_SCHEMA); - assertEquals(SECOND_SCHEMA, con.getSchema()); + connection.setSchema(SECOND_SCHEMA); + assertEquals(SECOND_SCHEMA, connection.getSchema()); statement.execute(String.format("use database %s", database)); statement.execute(String.format("use schema %s", schema)); - assertEquals(database, con.getCatalog()); - assertEquals(schema, con.getSchema()); + assertEquals(database, connection.getCatalog()); + assertEquals(schema, connection.getSchema()); } finally { statement.execute(String.format("drop database if exists %s", SECOND_DATABASE)); } @@ -253,40 +250,39 @@ public void testConnectionGetAndSetDBAndSchema() throws SQLException { @Test public void testConnectionClientInfo() throws SQLException { - try (Connection con = getConnection()) { - Properties property = con.getClientInfo(); - assertEquals(0, property.size()); - Properties clientInfo = new Properties(); - clientInfo.setProperty("name", "Peter"); - clientInfo.setProperty("description", "SNOWFLAKE JDBC"); - try { - con.setClientInfo(clientInfo); - fail("setClientInfo should fail for any parameter."); - } catch (SQLClientInfoException e) { - assertEquals(SqlState.INVALID_PARAMETER_VALUE, e.getSQLState()); - assertEquals(200047, e.getErrorCode()); - assertEquals(2, e.getFailedProperties().size()); - } - try { - con.setClientInfo("ApplicationName", "valueA"); - fail("setClientInfo should fail for any parameter."); - } catch (SQLClientInfoException e) { - assertEquals(SqlState.INVALID_PARAMETER_VALUE, e.getSQLState()); - assertEquals(200047, e.getErrorCode()); - assertEquals(1, e.getFailedProperties().size()); - } + Properties property = connection.getClientInfo(); + assertEquals(0, property.size()); + Properties clientInfo = new Properties(); + clientInfo.setProperty("name", "Peter"); + clientInfo.setProperty("description", "SNOWFLAKE JDBC"); + try { + connection.setClientInfo(clientInfo); + fail("setClientInfo should fail for any parameter."); + } catch (SQLClientInfoException e) { + assertEquals(SqlState.INVALID_PARAMETER_VALUE, e.getSQLState()); + assertEquals(200047, e.getErrorCode()); + assertEquals(2, e.getFailedProperties().size()); + } + try { + connection.setClientInfo("ApplicationName", "valueA"); + fail("setClientInfo should fail for any parameter."); + } catch (SQLClientInfoException e) { + assertEquals(SqlState.INVALID_PARAMETER_VALUE, e.getSQLState()); + assertEquals(200047, e.getErrorCode()); + assertEquals(1, e.getFailedProperties().size()); } } // only support get and set @Test public void testNetworkTimeout() throws SQLException { - try (Connection con = getConnection()) { - int millis = con.getNetworkTimeout(); - assertEquals(0, millis); - con.setNetworkTimeout(null, 200); - assertEquals(200, con.getNetworkTimeout()); - } + int millis = connection.getNetworkTimeout(); + assertEquals(0, millis); + connection.setNetworkTimeout(null, 200); + assertEquals(200, connection.getNetworkTimeout()); + // Reset timeout to 0 since we are reusing connection in tests + connection.setNetworkTimeout(null, 0); + assertEquals(0, millis); } @Test @@ -725,18 +721,14 @@ public void testHeartbeatFrequencyTooLarge() throws Exception { @Test public void testNativeSQL() throws Throwable { - try (Connection connection = getConnection()) { - // today returning the source SQL. - assertEquals("select 1", connection.nativeSQL("select 1")); - } + // today returning the source SQL. + assertEquals("select 1", connection.nativeSQL("select 1")); } @Test public void testGetTypeMap() throws Throwable { - try (Connection connection = getConnection()) { - // return an empty type map. setTypeMap is not supported. - assertEquals(Collections.emptyMap(), connection.getTypeMap()); - } + // return an empty type map. setTypeMap is not supported. + assertEquals(Collections.emptyMap(), connection.getTypeMap()); } @Test @@ -829,7 +821,6 @@ public void testReadDateAfterSplittingResultSet() throws Exception { @Test public void testResultSetsClosedByStatement() throws SQLException { - Connection connection = getConnection(); Statement statement2 = connection.createStatement(); ResultSet rs1 = statement2.executeQuery("select 2;"); ResultSet rs2 = statement2.executeQuery("select 2;"); @@ -846,7 +837,6 @@ public void testResultSetsClosedByStatement() throws SQLException { assertTrue(rs2.isClosed()); assertTrue(rs3.isClosed()); assertTrue(rs4.isClosed()); - connection.close(); } @Test diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataIT.java index 2ea144f3c..ce3130761 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataIT.java @@ -37,7 +37,7 @@ /** Database Metadata IT */ @Category(TestCategoryOthers.class) -public class DatabaseMetaDataIT extends BaseJDBCTest { +public class DatabaseMetaDataIT extends BaseJDBCWithSharedConnectionIT { private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)(?:\\.\\d+)+\\s*.*"); private static final String PI_PROCEDURE = @@ -65,152 +65,142 @@ public class DatabaseMetaDataIT extends BaseJDBCTest { @Test public void testGetConnection() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - assertEquals(connection, metaData.getConnection()); - } + DatabaseMetaData metaData = connection.getMetaData(); + assertEquals(connection, metaData.getConnection()); } @Test public void testDatabaseAndDriverInfo() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - - // identifiers - assertEquals("Snowflake", metaData.getDatabaseProductName()); - assertEquals("Snowflake", metaData.getDriverName()); - - // Snowflake JDBC driver version - String driverVersion = metaData.getDriverVersion(); - Matcher m = VERSION_PATTERN.matcher(driverVersion); - assertTrue(m.matches()); - int majorVersion = metaData.getDriverMajorVersion(); - int minorVersion = metaData.getDriverMinorVersion(); - assertEquals(m.group(1), String.valueOf(majorVersion)); - assertEquals(m.group(2), String.valueOf(minorVersion)); - } + DatabaseMetaData metaData = connection.getMetaData(); + + // identifiers + assertEquals("Snowflake", metaData.getDatabaseProductName()); + assertEquals("Snowflake", metaData.getDriverName()); + + // Snowflake JDBC driver version + String driverVersion = metaData.getDriverVersion(); + Matcher m = VERSION_PATTERN.matcher(driverVersion); + assertTrue(m.matches()); + int majorVersion = metaData.getDriverMajorVersion(); + int minorVersion = metaData.getDriverMinorVersion(); + assertEquals(m.group(1), String.valueOf(majorVersion)); + assertEquals(m.group(2), String.valueOf(minorVersion)); } @Test public void testGetCatalogs() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - assertEquals(".", metaData.getCatalogSeparator()); - assertEquals("database", metaData.getCatalogTerm()); - - ResultSet resultSet = metaData.getCatalogs(); - verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_CATALOGS); - assertTrue(resultSet.isBeforeFirst()); - - int cnt = 0; - Set allVisibleDatabases = new HashSet<>(); - while (resultSet.next()) { - allVisibleDatabases.add(resultSet.getString(1)); - if (cnt == 0) { - assertTrue(resultSet.isFirst()); - } - ++cnt; - try { - resultSet.isLast(); - fail("No isLast support for query based metadata"); - } catch (SQLFeatureNotSupportedException ex) { - // nop - } - try { - resultSet.isAfterLast(); - fail("No isAfterLast support for query based metadata"); - } catch (SQLFeatureNotSupportedException ex) { - // nop - } + DatabaseMetaData metaData = connection.getMetaData(); + assertEquals(".", metaData.getCatalogSeparator()); + assertEquals("database", metaData.getCatalogTerm()); + + ResultSet resultSet = metaData.getCatalogs(); + verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_CATALOGS); + assertTrue(resultSet.isBeforeFirst()); + + int cnt = 0; + Set allVisibleDatabases = new HashSet<>(); + while (resultSet.next()) { + allVisibleDatabases.add(resultSet.getString(1)); + if (cnt == 0) { + assertTrue(resultSet.isFirst()); } - assertThat(cnt, greaterThanOrEqualTo(1)); + ++cnt; try { - assertTrue(resultSet.isAfterLast()); - fail("The result set is automatically closed when all rows are fetched."); - } catch (SQLException ex) { - assertEquals((int) ErrorCode.RESULTSET_ALREADY_CLOSED.getMessageCode(), ex.getErrorCode()); + resultSet.isLast(); + fail("No isLast support for query based metadata"); + } catch (SQLFeatureNotSupportedException ex) { + // nop } try { resultSet.isAfterLast(); fail("No isAfterLast support for query based metadata"); - } catch (SQLException ex) { - assertEquals((int) ErrorCode.RESULTSET_ALREADY_CLOSED.getMessageCode(), ex.getErrorCode()); + } catch (SQLFeatureNotSupportedException ex) { + // nop } - resultSet.close(); // double closing does nothing. - resultSet.next(); // no exception + } + assertThat(cnt, greaterThanOrEqualTo(1)); + try { + assertTrue(resultSet.isAfterLast()); + fail("The result set is automatically closed when all rows are fetched."); + } catch (SQLException ex) { + assertEquals((int) ErrorCode.RESULTSET_ALREADY_CLOSED.getMessageCode(), ex.getErrorCode()); + } + try { + resultSet.isAfterLast(); + fail("No isAfterLast support for query based metadata"); + } catch (SQLException ex) { + assertEquals((int) ErrorCode.RESULTSET_ALREADY_CLOSED.getMessageCode(), ex.getErrorCode()); + } + resultSet.close(); // double closing does nothing. + resultSet.next(); // no exception - List allAccessibleDatabases = - getInfoBySQL("select database_name from information_schema.databases"); + List allAccessibleDatabases = + getInfoBySQL("select database_name from information_schema.databases"); - assertTrue(allVisibleDatabases.containsAll(allAccessibleDatabases)); - } + assertTrue(allVisibleDatabases.containsAll(allAccessibleDatabases)); } @Test public void testGetSchemas() throws Throwable { // CLIENT_METADATA_REQUEST_USE_CONNECTION_CTX = false - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - String currentSchema = connection.getSchema(); - assertEquals("schema", metaData.getSchemaTerm()); - Set schemas = new HashSet<>(); - try (ResultSet resultSet = metaData.getSchemas()) { - verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_SCHEMAS); - while (resultSet.next()) { - String schema = resultSet.getString(1); - if (currentSchema.equals(schema) || !TestUtil.isSchemaGeneratedInTests(schema)) { - schemas.add(schema); - } + DatabaseMetaData metaData = connection.getMetaData(); + String currentSchema = connection.getSchema(); + assertEquals("schema", metaData.getSchemaTerm()); + Set schemas = new HashSet<>(); + try (ResultSet resultSet = metaData.getSchemas()) { + verifyResultSetMetaDataColumns(resultSet, DBMetadataResultSetMetadata.GET_SCHEMAS); + while (resultSet.next()) { + String schema = resultSet.getString(1); + if (currentSchema.equals(schema) || !TestUtil.isSchemaGeneratedInTests(schema)) { + schemas.add(schema); } } - assertThat(schemas.size(), greaterThanOrEqualTo(1)); + } + assertThat(schemas.size(), greaterThanOrEqualTo(1)); - Set schemasInDb = new HashSet<>(); - try (ResultSet resultSet = metaData.getSchemas(connection.getCatalog(), "%")) { - while (resultSet.next()) { - String schema = resultSet.getString(1); - if (currentSchema.equals(schema) || !TestUtil.isSchemaGeneratedInTests(schema)) { - schemasInDb.add(schema); - } + Set schemasInDb = new HashSet<>(); + try (ResultSet resultSet = metaData.getSchemas(connection.getCatalog(), "%")) { + while (resultSet.next()) { + String schema = resultSet.getString(1); + if (currentSchema.equals(schema) || !TestUtil.isSchemaGeneratedInTests(schema)) { + schemasInDb.add(schema); } } - assertThat(schemasInDb.size(), greaterThanOrEqualTo(1)); - assertThat(schemas.size(), greaterThanOrEqualTo(schemasInDb.size())); - schemasInDb.forEach(schemaInDb -> assertThat(schemas, hasItem(schemaInDb))); - assertTrue(schemas.contains(currentSchema)); - assertTrue(schemasInDb.contains(currentSchema)); } + assertThat(schemasInDb.size(), greaterThanOrEqualTo(1)); + assertThat(schemas.size(), greaterThanOrEqualTo(schemasInDb.size())); + schemasInDb.forEach(schemaInDb -> assertThat(schemas, hasItem(schemaInDb))); + assertTrue(schemas.contains(currentSchema)); + assertTrue(schemasInDb.contains(currentSchema)); // CLIENT_METADATA_REQUEST_USE_CONNECTION_CTX = true try (Connection connection = getConnection(); Statement statement = connection.createStatement()) { statement.execute("alter SESSION set CLIENT_METADATA_REQUEST_USE_CONNECTION_CTX=true"); - DatabaseMetaData metaData = connection.getMetaData(); - assertEquals("schema", metaData.getSchemaTerm()); - try (ResultSet resultSet = metaData.getSchemas()) { - Set schemas = new HashSet<>(); + DatabaseMetaData metaData2 = connection.getMetaData(); + assertEquals("schema", metaData2.getSchemaTerm()); + try (ResultSet resultSet = metaData2.getSchemas()) { + Set schemas2 = new HashSet<>(); while (resultSet.next()) { - schemas.add(resultSet.getString(1)); + schemas2.add(resultSet.getString(1)); } - assertThat(schemas.size(), equalTo(1)); + assertThat(schemas2.size(), equalTo(1)); } } } @Test public void testGetTableTypes() throws Throwable { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - try (ResultSet resultSet = metaData.getTableTypes()) { - Set types = new HashSet<>(); - while (resultSet.next()) { - types.add(resultSet.getString(1)); - } - assertEquals(2, types.size()); - assertTrue(types.contains("TABLE")); - assertTrue(types.contains("VIEW")); + DatabaseMetaData metaData = connection.getMetaData(); + try (ResultSet resultSet = metaData.getTableTypes()) { + Set types = new HashSet<>(); + while (resultSet.next()) { + types.add(resultSet.getString(1)); } + assertEquals(2, types.size()); + assertTrue(types.contains("TABLE")); + assertTrue(types.contains("VIEW")); } } @@ -218,8 +208,7 @@ public void testGetTableTypes() throws Throwable { @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testGetTables() throws Throwable { Set tables = null; - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); final String targetTable = "T0"; @@ -271,8 +260,7 @@ public void testGetTables() throws Throwable { @Test public void testGetPrimarykeys() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); final String targetTable = "T0"; @@ -340,8 +328,7 @@ static void verifyResultSetMetaDataColumns( @Test public void testGetImportedKeys() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); final String targetTable1 = "T0"; @@ -386,8 +373,7 @@ public void testGetImportedKeys() throws Throwable { @Test public void testGetExportedKeys() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement(); ) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); final String targetTable1 = "T0"; @@ -433,8 +419,7 @@ public void testGetExportedKeys() throws Throwable { @Test public void testGetCrossReferences() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); final String targetTable1 = "T0"; @@ -482,8 +467,7 @@ public void testGetCrossReferences() throws Throwable { @Test public void testGetObjectsDoesNotExists() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); final String targetTable = "T0"; @@ -544,50 +528,45 @@ public void testGetObjectsDoesNotExists() throws Throwable { @Test public void testTypeInfo() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - ResultSet resultSet = metaData.getTypeInfo(); - resultSet.next(); - assertEquals("NUMBER", resultSet.getString(1)); - resultSet.next(); - assertEquals("INTEGER", resultSet.getString(1)); - resultSet.next(); - assertEquals("DOUBLE", resultSet.getString(1)); - resultSet.next(); - assertEquals("VARCHAR", resultSet.getString(1)); - resultSet.next(); - assertEquals("DATE", resultSet.getString(1)); - resultSet.next(); - assertEquals("TIME", resultSet.getString(1)); - resultSet.next(); - assertEquals("TIMESTAMP", resultSet.getString(1)); - resultSet.next(); - assertEquals("BOOLEAN", resultSet.getString(1)); - assertFalse(resultSet.next()); - } + DatabaseMetaData metaData = connection.getMetaData(); + ResultSet resultSet = metaData.getTypeInfo(); + resultSet.next(); + assertEquals("NUMBER", resultSet.getString(1)); + resultSet.next(); + assertEquals("INTEGER", resultSet.getString(1)); + resultSet.next(); + assertEquals("DOUBLE", resultSet.getString(1)); + resultSet.next(); + assertEquals("VARCHAR", resultSet.getString(1)); + resultSet.next(); + assertEquals("DATE", resultSet.getString(1)); + resultSet.next(); + assertEquals("TIME", resultSet.getString(1)); + resultSet.next(); + assertEquals("TIMESTAMP", resultSet.getString(1)); + resultSet.next(); + assertEquals("BOOLEAN", resultSet.getString(1)); + assertFalse(resultSet.next()); } @Test public void testProcedure() throws Throwable { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - assertEquals("procedure", metaData.getProcedureTerm()); - // no stored procedure support - assertTrue(metaData.supportsStoredProcedures()); - try (ResultSet resultSet = metaData.getProcedureColumns("%", "%", "%", "%")) { - assertEquals(0, getSizeOfResultSet(resultSet)); - } - try (ResultSet resultSet = metaData.getProcedures("%", "%", "%")) { - assertEquals(0, getSizeOfResultSet(resultSet)); - } + DatabaseMetaData metaData = connection.getMetaData(); + assertEquals("procedure", metaData.getProcedureTerm()); + // no stored procedure support + assertTrue(metaData.supportsStoredProcedures()); + try (ResultSet resultSet = metaData.getProcedureColumns("%", "%", "%", "%")) { + assertEquals(0, getSizeOfResultSet(resultSet)); + } + try (ResultSet resultSet = metaData.getProcedures("%", "%", "%")) { + assertEquals(0, getSizeOfResultSet(resultSet)); } } @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testGetTablePrivileges() throws Exception { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); try { @@ -641,8 +620,7 @@ public void testGetTablePrivileges() throws Exception { @Test public void testGetProcedures() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { try { String database = connection.getCatalog(); String schema = connection.getSchema(); @@ -670,197 +648,189 @@ public void testGetProcedures() throws SQLException { @Test public void testDatabaseMetadata() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - - String dbVersion = metaData.getDatabaseProductVersion(); - Matcher m = VERSION_PATTERN.matcher(dbVersion); - assertTrue(m.matches()); - int majorVersion = metaData.getDatabaseMajorVersion(); - int minorVersion = metaData.getDatabaseMinorVersion(); - assertEquals(m.group(1), String.valueOf(majorVersion)); - assertEquals(m.group(2), String.valueOf(minorVersion)); - - assertFalse(Strings.isNullOrEmpty(metaData.getSQLKeywords())); - assertFalse(Strings.isNullOrEmpty(metaData.getNumericFunctions())); - assertFalse(Strings.isNullOrEmpty(metaData.getStringFunctions())); - assertFalse(Strings.isNullOrEmpty(metaData.getSystemFunctions())); - assertFalse(Strings.isNullOrEmpty(metaData.getTimeDateFunctions())); - - assertEquals("\\", metaData.getSearchStringEscape()); - - assertTrue(metaData.getURL().startsWith("jdbc:snowflake://")); - assertFalse(metaData.allProceduresAreCallable()); - assertTrue(metaData.allTablesAreSelectable()); - assertTrue(metaData.dataDefinitionCausesTransactionCommit()); - assertFalse(metaData.dataDefinitionIgnoredInTransactions()); - assertFalse(metaData.deletesAreDetected(1)); - assertTrue(metaData.doesMaxRowSizeIncludeBlobs()); - assertTrue(metaData.supportsTransactions()); - assertEquals( - Connection.TRANSACTION_READ_COMMITTED, metaData.getDefaultTransactionIsolation()); - assertEquals("$", metaData.getExtraNameCharacters()); - assertEquals("\"", metaData.getIdentifierQuoteString()); - assertEquals(0, getSizeOfResultSet(metaData.getIndexInfo(null, null, null, true, true))); - assertEquals(EXPECTED_MAX_BINARY_LENGTH, metaData.getMaxBinaryLiteralLength()); - assertEquals(255, metaData.getMaxCatalogNameLength()); - assertEquals(EXPECTED_MAX_CHAR_LENGTH, metaData.getMaxCharLiteralLength()); - assertEquals(255, metaData.getMaxColumnNameLength()); - assertEquals(0, metaData.getMaxColumnsInGroupBy()); - assertEquals(0, metaData.getMaxColumnsInIndex()); - assertEquals(0, metaData.getMaxColumnsInOrderBy()); - assertEquals(0, metaData.getMaxColumnsInSelect()); - assertEquals(0, metaData.getMaxColumnsInTable()); - assertEquals(0, metaData.getMaxConnections()); - assertEquals(0, metaData.getMaxCursorNameLength()); - assertEquals(0, metaData.getMaxIndexLength()); - assertEquals(0, metaData.getMaxProcedureNameLength()); - assertEquals(0, metaData.getMaxRowSize()); - assertEquals(255, metaData.getMaxSchemaNameLength()); - assertEquals(0, metaData.getMaxStatementLength()); - assertEquals(0, metaData.getMaxStatements()); - assertEquals(255, metaData.getMaxTableNameLength()); - assertEquals(0, metaData.getMaxTablesInSelect()); - assertEquals(255, metaData.getMaxUserNameLength()); - assertEquals(0, getSizeOfResultSet(metaData.getTablePrivileges(null, null, null))); - // assertEquals("", metaData.getTimeDateFunctions()); - assertEquals(TestUtil.systemGetEnv("SNOWFLAKE_TEST_USER"), metaData.getUserName()); - assertFalse(metaData.insertsAreDetected(1)); - assertTrue(metaData.isCatalogAtStart()); - assertFalse(metaData.isReadOnly()); - assertTrue(metaData.nullPlusNonNullIsNull()); - assertFalse(metaData.nullsAreSortedAtEnd()); - assertFalse(metaData.nullsAreSortedAtStart()); - assertTrue(metaData.nullsAreSortedHigh()); - assertFalse(metaData.nullsAreSortedLow()); - assertFalse(metaData.othersDeletesAreVisible(1)); - assertFalse(metaData.othersInsertsAreVisible(1)); - assertFalse(metaData.othersUpdatesAreVisible(1)); - assertFalse(metaData.ownDeletesAreVisible(1)); - assertFalse(metaData.ownInsertsAreVisible(1)); - assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); - assertFalse(metaData.storesLowerCaseIdentifiers()); - assertFalse(metaData.storesLowerCaseQuotedIdentifiers()); - assertFalse(metaData.storesMixedCaseIdentifiers()); - assertTrue(metaData.storesMixedCaseQuotedIdentifiers()); - assertTrue(metaData.storesUpperCaseIdentifiers()); - assertFalse(metaData.storesUpperCaseQuotedIdentifiers()); - assertTrue(metaData.supportsAlterTableWithAddColumn()); - assertTrue(metaData.supportsAlterTableWithDropColumn()); - assertTrue(metaData.supportsANSI92EntryLevelSQL()); - assertFalse(metaData.supportsANSI92FullSQL()); - assertFalse(metaData.supportsANSI92IntermediateSQL()); - assertTrue(metaData.supportsBatchUpdates()); - assertTrue(metaData.supportsCatalogsInDataManipulation()); - assertFalse(metaData.supportsCatalogsInIndexDefinitions()); - assertFalse(metaData.supportsCatalogsInPrivilegeDefinitions()); - assertFalse(metaData.supportsCatalogsInProcedureCalls()); - assertTrue(metaData.supportsCatalogsInTableDefinitions()); - assertTrue(metaData.supportsColumnAliasing()); - assertFalse(metaData.supportsConvert()); - assertFalse(metaData.supportsConvert(1, 2)); - assertFalse(metaData.supportsCoreSQLGrammar()); - assertTrue(metaData.supportsCorrelatedSubqueries()); - assertTrue(metaData.supportsDataDefinitionAndDataManipulationTransactions()); - assertFalse(metaData.supportsDataManipulationTransactionsOnly()); - assertFalse(metaData.supportsDifferentTableCorrelationNames()); - assertTrue(metaData.supportsExpressionsInOrderBy()); - assertFalse(metaData.supportsExtendedSQLGrammar()); - assertTrue(metaData.supportsFullOuterJoins()); - assertFalse(metaData.supportsGetGeneratedKeys()); - assertTrue(metaData.supportsGroupBy()); - assertTrue(metaData.supportsGroupByBeyondSelect()); - assertFalse(metaData.supportsGroupByUnrelated()); - assertFalse(metaData.supportsIntegrityEnhancementFacility()); - assertFalse(metaData.supportsLikeEscapeClause()); - assertTrue(metaData.supportsLimitedOuterJoins()); - assertFalse(metaData.supportsMinimumSQLGrammar()); - assertFalse(metaData.supportsMixedCaseIdentifiers()); - assertTrue(metaData.supportsMixedCaseQuotedIdentifiers()); - assertFalse(metaData.supportsMultipleOpenResults()); - assertFalse(metaData.supportsMultipleResultSets()); - assertTrue(metaData.supportsMultipleTransactions()); - assertFalse(metaData.supportsNamedParameters()); - assertTrue(metaData.supportsNonNullableColumns()); - assertFalse(metaData.supportsOpenCursorsAcrossCommit()); - assertFalse(metaData.supportsOpenCursorsAcrossRollback()); - assertFalse(metaData.supportsOpenStatementsAcrossCommit()); - assertFalse(metaData.supportsOpenStatementsAcrossRollback()); - assertTrue(metaData.supportsOrderByUnrelated()); - assertTrue(metaData.supportsOuterJoins()); - assertFalse(metaData.supportsPositionedDelete()); - assertFalse(metaData.supportsPositionedUpdate()); - assertTrue( - metaData.supportsResultSetConcurrency( - ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)); - assertFalse( - metaData.supportsResultSetConcurrency( - ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)); - assertTrue(metaData.supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY)); - assertTrue(metaData.supportsResultSetHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT)); - assertFalse(metaData.supportsResultSetHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)); - assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, metaData.getResultSetHoldability()); - assertFalse(metaData.supportsSavepoints()); - assertTrue(metaData.supportsSchemasInDataManipulation()); - assertFalse(metaData.supportsSchemasInIndexDefinitions()); - assertFalse(metaData.supportsSchemasInPrivilegeDefinitions()); - assertFalse(metaData.supportsSchemasInProcedureCalls()); - assertTrue(metaData.supportsSchemasInTableDefinitions()); - assertFalse(metaData.supportsSelectForUpdate()); - assertFalse(metaData.supportsStatementPooling()); - assertTrue(metaData.supportsStoredFunctionsUsingCallSyntax()); - assertTrue(metaData.supportsSubqueriesInComparisons()); - assertTrue(metaData.supportsSubqueriesInExists()); - assertTrue(metaData.supportsSubqueriesInIns()); - assertFalse(metaData.supportsSubqueriesInQuantifieds()); - assertTrue(metaData.supportsTableCorrelationNames()); - assertTrue(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED)); - assertFalse( - metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ)); - assertFalse(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE)); - assertFalse( - metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED)); - assertTrue(metaData.supportsUnion()); - assertTrue(metaData.supportsUnionAll()); - assertFalse(metaData.updatesAreDetected(1)); - assertFalse(metaData.usesLocalFilePerTable()); - assertFalse(metaData.usesLocalFiles()); - } + DatabaseMetaData metaData = connection.getMetaData(); + + String dbVersion = metaData.getDatabaseProductVersion(); + Matcher m = VERSION_PATTERN.matcher(dbVersion); + assertTrue(m.matches()); + int majorVersion = metaData.getDatabaseMajorVersion(); + int minorVersion = metaData.getDatabaseMinorVersion(); + assertEquals(m.group(1), String.valueOf(majorVersion)); + assertEquals(m.group(2), String.valueOf(minorVersion)); + + assertFalse(Strings.isNullOrEmpty(metaData.getSQLKeywords())); + assertFalse(Strings.isNullOrEmpty(metaData.getNumericFunctions())); + assertFalse(Strings.isNullOrEmpty(metaData.getStringFunctions())); + assertFalse(Strings.isNullOrEmpty(metaData.getSystemFunctions())); + assertFalse(Strings.isNullOrEmpty(metaData.getTimeDateFunctions())); + + assertEquals("\\", metaData.getSearchStringEscape()); + + assertTrue(metaData.getURL().startsWith("jdbc:snowflake://")); + assertFalse(metaData.allProceduresAreCallable()); + assertTrue(metaData.allTablesAreSelectable()); + assertTrue(metaData.dataDefinitionCausesTransactionCommit()); + assertFalse(metaData.dataDefinitionIgnoredInTransactions()); + assertFalse(metaData.deletesAreDetected(1)); + assertTrue(metaData.doesMaxRowSizeIncludeBlobs()); + assertTrue(metaData.supportsTransactions()); + assertEquals(Connection.TRANSACTION_READ_COMMITTED, metaData.getDefaultTransactionIsolation()); + assertEquals("$", metaData.getExtraNameCharacters()); + assertEquals("\"", metaData.getIdentifierQuoteString()); + assertEquals(0, getSizeOfResultSet(metaData.getIndexInfo(null, null, null, true, true))); + assertEquals(EXPECTED_MAX_BINARY_LENGTH, metaData.getMaxBinaryLiteralLength()); + assertEquals(255, metaData.getMaxCatalogNameLength()); + assertEquals(EXPECTED_MAX_CHAR_LENGTH, metaData.getMaxCharLiteralLength()); + assertEquals(255, metaData.getMaxColumnNameLength()); + assertEquals(0, metaData.getMaxColumnsInGroupBy()); + assertEquals(0, metaData.getMaxColumnsInIndex()); + assertEquals(0, metaData.getMaxColumnsInOrderBy()); + assertEquals(0, metaData.getMaxColumnsInSelect()); + assertEquals(0, metaData.getMaxColumnsInTable()); + assertEquals(0, metaData.getMaxConnections()); + assertEquals(0, metaData.getMaxCursorNameLength()); + assertEquals(0, metaData.getMaxIndexLength()); + assertEquals(0, metaData.getMaxProcedureNameLength()); + assertEquals(0, metaData.getMaxRowSize()); + assertEquals(255, metaData.getMaxSchemaNameLength()); + assertEquals(0, metaData.getMaxStatementLength()); + assertEquals(0, metaData.getMaxStatements()); + assertEquals(255, metaData.getMaxTableNameLength()); + assertEquals(0, metaData.getMaxTablesInSelect()); + assertEquals(255, metaData.getMaxUserNameLength()); + assertEquals(0, getSizeOfResultSet(metaData.getTablePrivileges(null, null, null))); + // assertEquals("", metaData.getTimeDateFunctions()); + assertEquals(TestUtil.systemGetEnv("SNOWFLAKE_TEST_USER"), metaData.getUserName()); + assertFalse(metaData.insertsAreDetected(1)); + assertTrue(metaData.isCatalogAtStart()); + assertFalse(metaData.isReadOnly()); + assertTrue(metaData.nullPlusNonNullIsNull()); + assertFalse(metaData.nullsAreSortedAtEnd()); + assertFalse(metaData.nullsAreSortedAtStart()); + assertTrue(metaData.nullsAreSortedHigh()); + assertFalse(metaData.nullsAreSortedLow()); + assertFalse(metaData.othersDeletesAreVisible(1)); + assertFalse(metaData.othersInsertsAreVisible(1)); + assertFalse(metaData.othersUpdatesAreVisible(1)); + assertFalse(metaData.ownDeletesAreVisible(1)); + assertFalse(metaData.ownInsertsAreVisible(1)); + assertFalse(metaData.ownUpdatesAreVisible(ResultSet.TYPE_SCROLL_INSENSITIVE)); + assertFalse(metaData.storesLowerCaseIdentifiers()); + assertFalse(metaData.storesLowerCaseQuotedIdentifiers()); + assertFalse(metaData.storesMixedCaseIdentifiers()); + assertTrue(metaData.storesMixedCaseQuotedIdentifiers()); + assertTrue(metaData.storesUpperCaseIdentifiers()); + assertFalse(metaData.storesUpperCaseQuotedIdentifiers()); + assertTrue(metaData.supportsAlterTableWithAddColumn()); + assertTrue(metaData.supportsAlterTableWithDropColumn()); + assertTrue(metaData.supportsANSI92EntryLevelSQL()); + assertFalse(metaData.supportsANSI92FullSQL()); + assertFalse(metaData.supportsANSI92IntermediateSQL()); + assertTrue(metaData.supportsBatchUpdates()); + assertTrue(metaData.supportsCatalogsInDataManipulation()); + assertFalse(metaData.supportsCatalogsInIndexDefinitions()); + assertFalse(metaData.supportsCatalogsInPrivilegeDefinitions()); + assertFalse(metaData.supportsCatalogsInProcedureCalls()); + assertTrue(metaData.supportsCatalogsInTableDefinitions()); + assertTrue(metaData.supportsColumnAliasing()); + assertFalse(metaData.supportsConvert()); + assertFalse(metaData.supportsConvert(1, 2)); + assertFalse(metaData.supportsCoreSQLGrammar()); + assertTrue(metaData.supportsCorrelatedSubqueries()); + assertTrue(metaData.supportsDataDefinitionAndDataManipulationTransactions()); + assertFalse(metaData.supportsDataManipulationTransactionsOnly()); + assertFalse(metaData.supportsDifferentTableCorrelationNames()); + assertTrue(metaData.supportsExpressionsInOrderBy()); + assertFalse(metaData.supportsExtendedSQLGrammar()); + assertTrue(metaData.supportsFullOuterJoins()); + assertFalse(metaData.supportsGetGeneratedKeys()); + assertTrue(metaData.supportsGroupBy()); + assertTrue(metaData.supportsGroupByBeyondSelect()); + assertFalse(metaData.supportsGroupByUnrelated()); + assertFalse(metaData.supportsIntegrityEnhancementFacility()); + assertFalse(metaData.supportsLikeEscapeClause()); + assertTrue(metaData.supportsLimitedOuterJoins()); + assertFalse(metaData.supportsMinimumSQLGrammar()); + assertFalse(metaData.supportsMixedCaseIdentifiers()); + assertTrue(metaData.supportsMixedCaseQuotedIdentifiers()); + assertFalse(metaData.supportsMultipleOpenResults()); + assertFalse(metaData.supportsMultipleResultSets()); + assertTrue(metaData.supportsMultipleTransactions()); + assertFalse(metaData.supportsNamedParameters()); + assertTrue(metaData.supportsNonNullableColumns()); + assertFalse(metaData.supportsOpenCursorsAcrossCommit()); + assertFalse(metaData.supportsOpenCursorsAcrossRollback()); + assertFalse(metaData.supportsOpenStatementsAcrossCommit()); + assertFalse(metaData.supportsOpenStatementsAcrossRollback()); + assertTrue(metaData.supportsOrderByUnrelated()); + assertTrue(metaData.supportsOuterJoins()); + assertFalse(metaData.supportsPositionedDelete()); + assertFalse(metaData.supportsPositionedUpdate()); + assertTrue( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)); + assertFalse( + metaData.supportsResultSetConcurrency( + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)); + assertTrue(metaData.supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY)); + assertTrue(metaData.supportsResultSetHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT)); + assertFalse(metaData.supportsResultSetHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT)); + assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, metaData.getResultSetHoldability()); + assertFalse(metaData.supportsSavepoints()); + assertTrue(metaData.supportsSchemasInDataManipulation()); + assertFalse(metaData.supportsSchemasInIndexDefinitions()); + assertFalse(metaData.supportsSchemasInPrivilegeDefinitions()); + assertFalse(metaData.supportsSchemasInProcedureCalls()); + assertTrue(metaData.supportsSchemasInTableDefinitions()); + assertFalse(metaData.supportsSelectForUpdate()); + assertFalse(metaData.supportsStatementPooling()); + assertTrue(metaData.supportsStoredFunctionsUsingCallSyntax()); + assertTrue(metaData.supportsSubqueriesInComparisons()); + assertTrue(metaData.supportsSubqueriesInExists()); + assertTrue(metaData.supportsSubqueriesInIns()); + assertFalse(metaData.supportsSubqueriesInQuantifieds()); + assertTrue(metaData.supportsTableCorrelationNames()); + assertTrue(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED)); + assertFalse(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_REPEATABLE_READ)); + assertFalse(metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE)); + assertFalse( + metaData.supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED)); + assertTrue(metaData.supportsUnion()); + assertTrue(metaData.supportsUnionAll()); + assertFalse(metaData.updatesAreDetected(1)); + assertFalse(metaData.usesLocalFilePerTable()); + assertFalse(metaData.usesLocalFiles()); } @Test public void testOtherEmptyTables() throws Throwable { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); - // index is not supported. - try (ResultSet resultSet = metaData.getIndexInfo(null, null, null, true, true)) { - assertEquals(0, getSizeOfResultSet(resultSet)); - } - // UDT is not supported. - try (ResultSet resultSet = metaData.getUDTs(null, null, null, new int[] {})) { - assertEquals(0, getSizeOfResultSet(resultSet)); - } + // index is not supported. + try (ResultSet resultSet = metaData.getIndexInfo(null, null, null, true, true)) { + assertEquals(0, getSizeOfResultSet(resultSet)); + } + // UDT is not supported. + try (ResultSet resultSet = metaData.getUDTs(null, null, null, new int[] {})) { + assertEquals(0, getSizeOfResultSet(resultSet)); } } @Test public void testFeatureNotSupportedException() throws Throwable { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - expectFeatureNotSupportedException( - () -> metaData.getBestRowIdentifier(null, null, null, 0, true)); - expectFeatureNotSupportedException(() -> metaData.getVersionColumns(null, null, null)); - expectFeatureNotSupportedException(() -> metaData.getSuperTypes(null, null, null)); - expectFeatureNotSupportedException(() -> metaData.getSuperTables(null, null, null)); - expectFeatureNotSupportedException(() -> metaData.getAttributes(null, null, null, null)); - expectFeatureNotSupportedException(metaData::getRowIdLifetime); - expectFeatureNotSupportedException(metaData::autoCommitFailureClosesAllResultSets); - expectFeatureNotSupportedException(metaData::getClientInfoProperties); - expectFeatureNotSupportedException(() -> metaData.getPseudoColumns(null, null, null, null)); - expectFeatureNotSupportedException(metaData::generatedKeyAlwaysReturned); - expectFeatureNotSupportedException( - () -> metaData.isWrapperFor(SnowflakeDatabaseMetaData.class)); - } + DatabaseMetaData metaData = connection.getMetaData(); + expectFeatureNotSupportedException( + () -> metaData.getBestRowIdentifier(null, null, null, 0, true)); + expectFeatureNotSupportedException(() -> metaData.getVersionColumns(null, null, null)); + expectFeatureNotSupportedException(() -> metaData.getSuperTypes(null, null, null)); + expectFeatureNotSupportedException(() -> metaData.getSuperTables(null, null, null)); + expectFeatureNotSupportedException(() -> metaData.getAttributes(null, null, null, null)); + expectFeatureNotSupportedException(metaData::getRowIdLifetime); + expectFeatureNotSupportedException(metaData::autoCommitFailureClosesAllResultSets); + expectFeatureNotSupportedException(metaData::getClientInfoProperties); + expectFeatureNotSupportedException(() -> metaData.getPseudoColumns(null, null, null, null)); + expectFeatureNotSupportedException(metaData::generatedKeyAlwaysReturned); + expectFeatureNotSupportedException( + () -> metaData.isWrapperFor(SnowflakeDatabaseMetaData.class)); } } diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataLatestIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataLatestIT.java index bebe3d8f4..082907502 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataLatestIT.java @@ -36,6 +36,7 @@ import net.snowflake.client.category.TestCategoryOthers; import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SFSessionProperty; +import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -47,7 +48,7 @@ * the latest and oldest supported driver run the tests. */ @Category(TestCategoryOthers.class) -public class DatabaseMetaDataLatestIT extends BaseJDBCTest { +public class DatabaseMetaDataLatestIT extends BaseJDBCWithSharedConnectionIT { private static final String TEST_PROC = "create or replace procedure testproc(param1 float, param2 string)\n" + " returns varchar\n" @@ -80,12 +81,32 @@ public class DatabaseMetaDataLatestIT extends BaseJDBCTest { private static final String ENABLE_PATTERN_SEARCH = SFSessionProperty.ENABLE_PATTERN_SEARCH.getPropertyKey(); + private static final String startingSchema; + private static final String startingDatabase; + + static { + try { + startingSchema = connection.getSchema(); + startingDatabase = connection.getCatalog(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + /** Create catalog and schema for tests with double quotes */ public void createDoubleQuotedSchemaAndCatalog(Statement statement) throws SQLException { statement.execute("create or replace database \"dbwith\"\"quotes\""); statement.execute("create or replace schema \"dbwith\"\"quotes\".\"schemawith\"\"quotes\""); } + @Before + public void setUp() throws SQLException { + try (Statement stmt = connection.createStatement()) { + stmt.execute("USE DATABASE " + startingDatabase); + stmt.execute("USE SCHEMA " + startingSchema); + } + } + /** * Tests for getFunctions * @@ -199,9 +220,8 @@ public void testUseConnectionCtx() throws Exception { */ @Test public void testDoubleQuotedDatabaseAndSchema() throws Exception { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - String database = con.getCatalog(); + try (Statement statement = connection.createStatement()) { + String database = startingDatabase; // To query the schema and table, we can use a normal java escaped quote. Wildcards are also // escaped here String schemaRandomPart = SnowflakeUtil.randomAlphaNumeric(5); @@ -226,7 +246,7 @@ public void testDoubleQuotedDatabaseAndSchema() throws Exception { statement.execute( "create or replace table \"TESTTABLE_\"\"WITH_QUOTES\"\"\" (AMOUNT number," + " \"COL_\"\"QUOTED\"\"\" string)"); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet rs = metaData.getTables(database, querySchema, queryTable, null)) { // Assert 1 row returned for the testtable_"with_quotes" assertEquals(1, getSizeOfResultSet(rs)); @@ -254,13 +274,12 @@ public void testDoubleQuotedDatabaseAndSchema() throws Exception { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testDoubleQuotedDatabaseInGetSchemas() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { // Create a database with double quotes inside the database name statement.execute("create or replace database \"\"\"quoteddb\"\"\""); // Create a database, lowercase, with no double quotes inside the database name statement.execute("create or replace database \"unquoteddb\""); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); // Assert 2 rows returned for the PUBLIC and INFORMATION_SCHEMA schemas inside database try (ResultSet rs = metaData.getSchemas("\"quoteddb\"", null)) { assertEquals(2, getSizeOfResultSet(rs)); @@ -283,14 +302,13 @@ public void testDoubleQuotedDatabaseInGetSchemas() throws SQLException { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testDoubleQuotedDatabaseInGetTables() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { // Create a database with double quotes inside the database name createDoubleQuotedSchemaAndCatalog(statement); // Create a table with two columns statement.execute( "create or replace table \"dbwith\"\"quotes\".\"schemawith\"\"quotes\".\"testtable\" (col1 string, col2 string)"); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet rs = metaData.getTables("dbwith\"quotes", "schemawith\"quotes", null, null)) { assertEquals(1, getSizeOfResultSet(rs)); } @@ -300,14 +318,13 @@ public void testDoubleQuotedDatabaseInGetTables() throws SQLException { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testDoubleQuotedDatabaseInGetColumns() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { // Create a database and schema with double quotes inside the database name createDoubleQuotedSchemaAndCatalog(statement); // Create a table with two columns statement.execute( "create or replace table \"dbwith\"\"quotes\".\"schemawith\"\"quotes\".\"testtable\" (col1 string, col2 string)"); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet rs = metaData.getColumns("dbwith\"quotes", "schemawith\"quotes", null, null)) { assertEquals(2, getSizeOfResultSet(rs)); } @@ -317,8 +334,7 @@ public void testDoubleQuotedDatabaseInGetColumns() throws SQLException { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testDoubleQuotedDatabaseforGetPrimaryKeysAndForeignKeys() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { // Create a database and schema with double quotes inside the database name createDoubleQuotedSchemaAndCatalog(statement); // Create a table with a primary key constraint @@ -328,7 +344,7 @@ public void testDoubleQuotedDatabaseforGetPrimaryKeysAndForeignKeys() throws SQL // key constraint statement.execute( "create or replace table \"dbwith\"\"quotes\".\"schemawith\"\"quotes\".\"test2\" (col_a integer not null, col_b integer not null, constraint fkey_1 foreign key (col_a, col_b) references \"test1\" (col1, col2) not enforced)"); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet rs = metaData.getPrimaryKeys("dbwith\"quotes", "schemawith\"quotes", null)) { // Assert 2 rows are returned for primary key constraint for table and schema with quotes assertEquals(2, getSizeOfResultSet(rs)); @@ -376,15 +392,14 @@ public void testDoubleQuotedDatabaseforGetPrimaryKeysAndForeignKeysWithPatternSe @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testDoubleQuotedDatabaseInGetProcedures() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { // Create a database and schema with double quotes inside the database name createDoubleQuotedSchemaAndCatalog(statement); // Create a procedure statement.unwrap(SnowflakeStatement.class).setParameter("MULTI_STATEMENT_COUNT", 3); statement.execute( "USE DATABASE \"dbwith\"\"quotes\"; USE SCHEMA \"schemawith\"\"quotes\"; " + TEST_PROC); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet rs = metaData.getProcedures("dbwith\"quotes", null, "TESTPROC")) { assertEquals(1, getSizeOfResultSet(rs)); } @@ -394,14 +409,13 @@ public void testDoubleQuotedDatabaseInGetProcedures() throws SQLException { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testDoubleQuotedDatabaseInGetTablePrivileges() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { // Create a database and schema with double quotes inside the database name createDoubleQuotedSchemaAndCatalog(statement); // Create a table under the current user and role statement.execute( "create or replace table \"dbwith\"\"quotes\".\"schemawith\"\"quotes\".\"testtable\" (col1 string, col2 string)"); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet rs = metaData.getTablePrivileges("dbwith\"quotes", null, "%")) { assertEquals(1, getSizeOfResultSet(rs)); } @@ -455,9 +469,8 @@ public void testGetFunctionSqlInjectionProtection() throws Throwable { */ @Test public void testGetProcedureColumnsWildcards() throws Exception { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - String database = con.getCatalog(); + try (Statement statement = connection.createStatement()) { + String database = startingDatabase; String schemaPrefix = TestUtil.GENERATED_SCHEMA_PREFIX + SnowflakeUtil.randomAlphaNumeric(5).toUpperCase(); String schema1 = schemaPrefix + "SCH1"; @@ -473,7 +486,7 @@ public void testGetProcedureColumnsWildcards() throws Exception { () -> { statement.execute(TEST_PROC); // Create 2 schemas, each with the same stored procedure declared in them - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet rs = metaData.getProcedureColumns( database, schemaPrefix + "SCH_", "TESTPROC", "PARAM1")) { @@ -489,17 +502,15 @@ public void testGetProcedureColumnsWildcards() throws Exception { @Test public void testGetFunctions() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metadata = connection.getMetaData(); - String supportedStringFuncs = metadata.getStringFunctions(); - assertEquals(StringFunctionsSupported, supportedStringFuncs); + DatabaseMetaData metadata = connection.getMetaData(); + String supportedStringFuncs = metadata.getStringFunctions(); + assertEquals(StringFunctionsSupported, supportedStringFuncs); - String supportedNumberFuncs = metadata.getNumericFunctions(); - assertEquals(NumericFunctionsSupported, supportedNumberFuncs); + String supportedNumberFuncs = metadata.getNumericFunctions(); + assertEquals(NumericFunctionsSupported, supportedNumberFuncs); - String supportedSystemFuncs = metadata.getSystemFunctions(); - assertEquals(SystemFunctionsSupported, supportedSystemFuncs); - } + String supportedSystemFuncs = metadata.getSystemFunctions(); + assertEquals(SystemFunctionsSupported, supportedSystemFuncs); } @Test @@ -553,10 +564,9 @@ public void testGetStringValueFromColumnDef() throws SQLException { @Test public void testGetColumnsNullable() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - String database = connection.getCatalog(); - String schema = connection.getSchema(); + try (Statement statement = connection.createStatement()) { + String database = startingDatabase; + String schema = startingSchema; final String targetTable = "T0"; statement.execute( @@ -748,10 +758,9 @@ public void testSessionDatabaseParameter() throws Throwable { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testGetFunctionColumns() throws Exception { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - String database = connection.getCatalog(); - String schema = connection.getSchema(); + try (Statement statement = connection.createStatement()) { + String database = startingDatabase; + String schema = startingSchema; /* Create a table and put values into it */ statement.execute( @@ -1010,8 +1019,7 @@ public void testGetFunctionColumns() throws Exception { @Test public void testHandlingSpecialChars() throws Exception { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); DatabaseMetaData metaData = connection.getMetaData(); @@ -1120,9 +1128,8 @@ public void testHandlingSpecialChars() throws Exception { @Test public void testUnderscoreInSchemaNamePatternForPrimaryAndForeignKeys() throws Exception { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - String database = con.getCatalog(); + try (Statement statement = connection.createStatement()) { + String database = startingDatabase; TestUtil.withRandomSchema( statement, customSchema -> { @@ -1132,7 +1139,7 @@ public void testUnderscoreInSchemaNamePatternForPrimaryAndForeignKeys() throws E "create or replace table PK_TEST (c1 int PRIMARY KEY, c2 VARCHAR(10))"); statement.execute( "create or replace table FK_TEST (c1 int REFERENCES PK_TEST(c1), c2 VARCHAR(10))"); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet rs = metaData.getPrimaryKeys(database, escapedSchema, null)) { assertEquals(1, getSizeOfResultSet(rs)); } @@ -1215,8 +1222,7 @@ public void testTimestampWithTimezoneDataType() throws Exception { @Test public void testGetColumns() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); String schema = connection.getSchema(); final String targetTable = "T0"; @@ -1610,18 +1616,17 @@ public void testGetColumns() throws Throwable { public void testGetStreams() throws SQLException { final String targetStream = "S0"; final String targetTable = "T0"; - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - String database = con.getCatalog(); - String schema = con.getSchema(); - String owner = con.unwrap(SnowflakeConnectionV1.class).getSFBaseSession().getRole(); + try (Statement statement = connection.createStatement()) { + String database = connection.getCatalog(); + String schema = connection.getSchema(); + String owner = connection.unwrap(SnowflakeConnectionV1.class).getSFBaseSession().getRole(); String tableName = database + "." + schema + "." + targetTable; try { statement.execute("create or replace table " + targetTable + "(C1 int)"); statement.execute("create or replace stream " + targetStream + " on table " + targetTable); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); // match stream try (ResultSet resultSet = @@ -1664,21 +1669,18 @@ public void testGetStreams() throws SQLException { @Test @Ignore public void testGetProceduresWithReaderAccount() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metadata = connection.getMetaData(); - try (ResultSet rs = metadata.getProcedures(null, null, null)) { - assertEquals(0, getSizeOfResultSet(rs)); - } + DatabaseMetaData metadata = connection.getMetaData(); + try (ResultSet rs = metadata.getProcedures(null, null, null)) { + assertEquals(0, getSizeOfResultSet(rs)); } } @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testGetProcedureColumns() throws Exception { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { - String database = connection.getCatalog(); - String schema = connection.getSchema(); + try (Statement statement = connection.createStatement()) { + String database = startingDatabase; + String schema = startingSchema; try { statement.execute(PI_PROCEDURE); DatabaseMetaData metaData = connection.getMetaData(); @@ -1724,8 +1726,7 @@ in the current database and schema. It will return all rows as well (1 row per r @Test public void testGetProcedureColumnsReturnsResultSet() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { + try (Statement statement = connection.createStatement()) { try { statement.execute( "create or replace table testtable (id int, name varchar(20), address varchar(20));"); @@ -1739,9 +1740,9 @@ public void testGetProcedureColumnsReturnsResultSet() throws SQLException { + " begin\n" + " return table(res);\n" + " end';"); - DatabaseMetaData metaData = con.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); try (ResultSet res = - metaData.getProcedureColumns(con.getCatalog(), null, "PROCTEST", "%")) { + metaData.getProcedureColumns(connection.getCatalog(), null, "PROCTEST", "%")) { res.next(); assertEquals("PROCTEST", res.getString("PROCEDURE_NAME")); assertEquals("id", res.getString("COLUMN_NAME")); @@ -1772,12 +1773,12 @@ public void testGetProcedureColumnsReturnsResultSet() throws SQLException { @Test public void testGetProcedureColumnsReturnsValue() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement(); ) { - DatabaseMetaData metaData = con.getMetaData(); + try (Statement statement = connection.createStatement(); ) { + DatabaseMetaData metaData = connection.getMetaData(); // create a procedure with no parameters that has a return value statement.execute(PI_PROCEDURE); - try (ResultSet res = metaData.getProcedureColumns(con.getCatalog(), null, "GETPI", "%")) { + try (ResultSet res = + metaData.getProcedureColumns(connection.getCatalog(), null, "GETPI", "%")) { res.next(); assertEquals("GETPI", res.getString("PROCEDURE_NAME")); assertEquals("", res.getString("COLUMN_NAME")); @@ -1790,7 +1791,7 @@ public void testGetProcedureColumnsReturnsValue() throws SQLException { // create a procedure that returns the value of the argument that is passed in statement.execute(MESSAGE_PROCEDURE); try (ResultSet res = - metaData.getProcedureColumns(con.getCatalog(), null, "MESSAGE_PROC", "%")) { + metaData.getProcedureColumns(connection.getCatalog(), null, "MESSAGE_PROC", "%")) { res.next(); assertEquals("MESSAGE_PROC", res.getString("PROCEDURE_NAME")); assertEquals("", res.getString("COLUMN_NAME")); @@ -1813,9 +1814,8 @@ public void testGetProcedureColumnsReturnsValue() throws SQLException { @Test public void testGetProcedureColumnsReturnsNull() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement(); ) { - DatabaseMetaData metaData = con.getMetaData(); + try (Statement statement = connection.createStatement(); ) { + DatabaseMetaData metaData = connection.getMetaData(); // The CREATE PROCEDURE statement must include a RETURNS clause that defines a return type, // even // if the procedure does not explicitly return anything. @@ -1830,7 +1830,7 @@ public void testGetProcedureColumnsReturnsNull() throws SQLException { + "snowflake.execute({sqlText: sqlcommand}); \n" + "';"); try (ResultSet res = - metaData.getProcedureColumns(con.getCatalog(), null, "INSERTPROC", "%")) { + metaData.getProcedureColumns(connection.getCatalog(), null, "INSERTPROC", "%")) { res.next(); // the procedure will return null as the value but column type will be varchar. assertEquals("INSERTPROC", res.getString("PROCEDURE_NAME")); @@ -1847,10 +1847,8 @@ public void testGetProcedureColumnsReturnsNull() throws SQLException { @Test public void testUpdateLocatorsCopyUnsupported() throws SQLException { - try (Connection con = getConnection()) { - DatabaseMetaData metaData = con.getMetaData(); - assertFalse(metaData.locatorsUpdateCopy()); - } + DatabaseMetaData metaData = connection.getMetaData(); + assertFalse(metaData.locatorsUpdateCopy()); } /** @@ -2106,8 +2104,7 @@ public void testPatternSearchAllowedForPrimaryAndForeignKeys() throws Exception final String table1 = "PATTERN_SEARCH_TABLE1"; final String table2 = "PATTERN_SEARCH_TABLE2"; - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { String schemaRandomPart = SnowflakeUtil.randomAlphaNumeric(5); String schemaName = "\"" @@ -2327,28 +2324,23 @@ public void testPatternSearchAllowedForPrimaryAndForeignKeys() throws Exception */ @Test public void testGetJDBCVersion() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); + DatabaseMetaData metaData = connection.getMetaData(); - // JDBC x.x compatible - assertEquals(4, metaData.getJDBCMajorVersion()); - assertEquals(2, metaData.getJDBCMinorVersion()); - } + // JDBC x.x compatible + assertEquals(4, metaData.getJDBCMajorVersion()); + assertEquals(2, metaData.getJDBCMinorVersion()); } /** Added in > 3.15.1 */ @Test public void testKeywordsCount() throws SQLException { - try (Connection connection = getConnection()) { - DatabaseMetaData metaData = connection.getMetaData(); - assertEquals(43, metaData.getSQLKeywords().split(",").length); - } + DatabaseMetaData metaData = connection.getMetaData(); + assertEquals(43, metaData.getSQLKeywords().split(",").length); } /** Added in > 3.16.1 */ @Test public void testVectorDimension() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + try (Statement statement = connection.createStatement()) { statement.execute( "create or replace table JDBC_VECTOR(text_col varchar(32), float_vec VECTOR(FLOAT, 256), int_vec VECTOR(INT, 16))"); DatabaseMetaData metaData = connection.getMetaData(); From 231b56975ceeeb92d84bb2a1309c0010300f1cb5 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:52:08 +0200 Subject: [PATCH 25/53] Bump version to 3.19.1 for release (#1939) --- CHANGELOG.rst | 4 ++++ FIPS/pom.xml | 4 ++-- parent-pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b83a77291..72c788c36 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,7 @@ +**JDBC Driver 3.19.1** + +- \||Please Refer to Release Notes at https://docs.snowflake.com/en/release-notes/clients-drivers/jdbc + **JDBC Driver 3.19.0** - \||Please Refer to Release Notes at https://docs.snowflake.com/en/release-notes/clients-drivers/jdbc diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 425d263df..eacc4e71f 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -5,12 +5,12 @@ net.snowflake snowflake-jdbc-parent - 3.19.1-SNAPSHOT + 3.19.1 ../parent-pom.xml snowflake-jdbc-fips - 3.19.1-SNAPSHOT + 3.19.1 jar snowflake-jdbc-fips diff --git a/parent-pom.xml b/parent-pom.xml index cc9b0b4d9..f9b85767e 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -5,7 +5,7 @@ net.snowflake snowflake-jdbc-parent - 3.19.1-SNAPSHOT + 3.19.1 pom diff --git a/pom.xml b/pom.xml index 2fd7f9216..c2c4e218d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ net.snowflake snowflake-jdbc-parent - 3.19.1-SNAPSHOT + 3.19.1 ./parent-pom.xml ${artifactId} - 3.19.1-SNAPSHOT + 3.19.1 jar ${artifactId} From 3d3f401e1bfb3ed544bc40fc0a9a9e1f16f56299 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Fri, 25 Oct 2024 21:07:36 +0200 Subject: [PATCH 26/53] Prepare next development version (#1940) Co-authored-by: Przemyslaw Motacki --- FIPS/pom.xml | 4 ++-- parent-pom.xml | 2 +- pom.xml | 4 ++-- src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index eacc4e71f..cbc6132af 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -5,12 +5,12 @@ net.snowflake snowflake-jdbc-parent - 3.19.1 + 3.19.2-SNAPSHOT ../parent-pom.xml snowflake-jdbc-fips - 3.19.1 + 3.19.2-SNAPSHOT jar snowflake-jdbc-fips diff --git a/parent-pom.xml b/parent-pom.xml index f9b85767e..b8f46b00f 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -5,7 +5,7 @@ net.snowflake snowflake-jdbc-parent - 3.19.1 + 3.19.2-SNAPSHOT pom diff --git a/pom.xml b/pom.xml index c2c4e218d..c7542a1fe 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ net.snowflake snowflake-jdbc-parent - 3.19.1 + 3.19.2-SNAPSHOT ./parent-pom.xml ${artifactId} - 3.19.1 + 3.19.2-SNAPSHOT jar ${artifactId} diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java index 060ac977e..bd03ddd55 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java @@ -37,7 +37,7 @@ public class SnowflakeDriver implements Driver { static SnowflakeDriver INSTANCE; public static final Properties EMPTY_PROPERTIES = new Properties(); - public static String implementVersion = "3.19.1"; + public static String implementVersion = "3.19.2"; static int majorVersion = 0; static int minorVersion = 0; From 83e5849103907f6b48a96c023233f2ca3e024a30 Mon Sep 17 00:00:00 2001 From: Dawid Heyman Date: Mon, 28 Oct 2024 07:07:14 +0100 Subject: [PATCH 27/53] SNOW-1757822: Allow JDBC to handle ZSTD decompression (#1932) --- FIPS/scripts/check_content.sh | 4 +- ci/scripts/check_content.sh | 6 +- linkage-checker-exclusion-rules.xml | 5 -- parent-pom.xml | 10 +++ .../client/jdbc/CompressedStreamFactory.java | 38 +++++++++ .../jdbc/DefaultResultStreamProvider.java | 36 +++------ .../jdbc/CompressedStreamFactoryTest.java | 80 +++++++++++++++++++ thin_public_pom.xml | 6 ++ 8 files changed, 148 insertions(+), 37 deletions(-) create mode 100644 src/main/java/net/snowflake/client/jdbc/CompressedStreamFactory.java create mode 100644 src/test/java/net/snowflake/client/jdbc/CompressedStreamFactoryTest.java diff --git a/FIPS/scripts/check_content.sh b/FIPS/scripts/check_content.sh index 8b818b1b4..565159073 100755 --- a/FIPS/scripts/check_content.sh +++ b/FIPS/scripts/check_content.sh @@ -1,12 +1,12 @@ #!/bin/bash -e -# scripts used to check if all dependency is shaded into snowflake internal path +# scripts used to check if all dependencies are shaded into snowflake internal path set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types; then +if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^com/github/" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi diff --git a/ci/scripts/check_content.sh b/ci/scripts/check_content.sh index a9c0768b6..3a0747be2 100755 --- a/ci/scripts/check_content.sh +++ b/ci/scripts/check_content.sh @@ -8,12 +8,12 @@ set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types; then +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^com/github/" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake"; then - echo "[ERROR] JDBC jar includes multi release classes not under the snowflake namespace" +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake" | grep -v -E "^META-INF/versions/.*/com/github" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then + echo "[ERROR] JDBC jar includes multi-release classes not under the snowflake namespace" exit 1 fi diff --git a/linkage-checker-exclusion-rules.xml b/linkage-checker-exclusion-rules.xml index 65affa44a..64b5860c2 100644 --- a/linkage-checker-exclusion-rules.xml +++ b/linkage-checker-exclusion-rules.xml @@ -19,11 +19,6 @@ Optional - - - - Optional - diff --git a/parent-pom.xml b/parent-pom.xml index b8f46b00f..855d171db 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -19,6 +19,7 @@ 1.10.0 4.5.14 4.4.16 + 1.5.6-5 17.0.0 9.3 1.8.1 @@ -327,6 +328,11 @@ httpcore ${apache.httpcore.version} + + com.github.luben + zstd-jni + ${zstd-jni.version} + org.apache.tika tika-core @@ -650,6 +656,10 @@ org.apache.httpcomponents httpcore + + com.github.luben + zstd-jni + org.apache.tika tika-core diff --git a/src/main/java/net/snowflake/client/jdbc/CompressedStreamFactory.java b/src/main/java/net/snowflake/client/jdbc/CompressedStreamFactory.java new file mode 100644 index 000000000..ebb376db9 --- /dev/null +++ b/src/main/java/net/snowflake/client/jdbc/CompressedStreamFactory.java @@ -0,0 +1,38 @@ +package net.snowflake.client.jdbc; + +import static net.snowflake.client.core.Constants.MB; +import static net.snowflake.common.core.FileCompressionType.GZIP; +import static net.snowflake.common.core.FileCompressionType.ZSTD; + +import com.github.luben.zstd.ZstdInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.GZIPInputStream; +import net.snowflake.common.core.SqlState; +import org.apache.http.Header; + +class CompressedStreamFactory { + + private static final int STREAM_BUFFER_SIZE = MB; + + /** + * Determine the format of the response, if it is not either plain text or gzip, raise an error. + */ + public InputStream createBasedOnEncodingHeader(InputStream is, Header encoding) + throws IOException, SnowflakeSQLException { + if (encoding != null) { + if (GZIP.name().equalsIgnoreCase(encoding.getValue())) { + return new GZIPInputStream(is, STREAM_BUFFER_SIZE); + } else if (ZSTD.name().equalsIgnoreCase(encoding.getValue())) { + return new ZstdInputStream(is); + } else { + throw new SnowflakeSQLException( + SqlState.INTERNAL_ERROR, + ErrorCode.INTERNAL_ERROR.getMessageCode(), + "Exception: unexpected compression got " + encoding.getValue()); + } + } else { + return DefaultResultStreamProvider.detectGzipAndGetStream(is); + } + } +} diff --git a/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java b/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java index 3ee556bb4..e7a1e8a0c 100644 --- a/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java +++ b/src/main/java/net/snowflake/client/jdbc/DefaultResultStreamProvider.java @@ -1,7 +1,5 @@ package net.snowflake.client.jdbc; -import static net.snowflake.client.core.Constants.MB; - import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; @@ -34,7 +32,11 @@ public class DefaultResultStreamProvider implements ResultStreamProvider { // SSE-C algorithm value private static final String SSE_C_AES = "AES256"; - private static final int STREAM_BUFFER_SIZE = MB; + private CompressedStreamFactory compressedStreamFactory; + + public DefaultResultStreamProvider() { + this.compressedStreamFactory = new CompressedStreamFactory(); + } @Override public InputStream getInputStream(ChunkDownloadContext context) throws Exception { @@ -71,9 +73,11 @@ public InputStream getInputStream(ChunkDownloadContext context) throws Exception InputStream inputStream; final HttpEntity entity = response.getEntity(); + Header encoding = response.getFirstHeader("Content-Encoding"); try { - // read the chunk data - inputStream = detectContentEncodingAndGetInputStream(response, entity.getContent()); + // create stream based on compression type + inputStream = + compressedStreamFactory.createBasedOnEncodingHeader(entity.getContent(), encoding); } catch (Exception ex) { logger.error("Failed to decompress data: {}", response); @@ -144,28 +148,6 @@ else if (context.getQrmk() != null) { return response; } - private InputStream detectContentEncodingAndGetInputStream(HttpResponse response, InputStream is) - throws IOException, SnowflakeSQLException { - InputStream inputStream = is; // Determine the format of the response, if it is not - // either plain text or gzip, raise an error. - Header encoding = response.getFirstHeader("Content-Encoding"); - if (encoding != null) { - if ("gzip".equalsIgnoreCase(encoding.getValue())) { - /* specify buffer size for GZIPInputStream */ - inputStream = new GZIPInputStream(is, STREAM_BUFFER_SIZE); - } else { - throw new SnowflakeSQLException( - SqlState.INTERNAL_ERROR, - ErrorCode.INTERNAL_ERROR.getMessageCode(), - "Exception: unexpected compression got " + encoding.getValue()); - } - } else { - inputStream = detectGzipAndGetStream(is); - } - - return inputStream; - } - public static InputStream detectGzipAndGetStream(InputStream is) throws IOException { PushbackInputStream pb = new PushbackInputStream(is, 2); byte[] signature = new byte[2]; diff --git a/src/test/java/net/snowflake/client/jdbc/CompressedStreamFactoryTest.java b/src/test/java/net/snowflake/client/jdbc/CompressedStreamFactoryTest.java new file mode 100644 index 000000000..86eb5764a --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/CompressedStreamFactoryTest.java @@ -0,0 +1,80 @@ +package net.snowflake.client.jdbc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.github.luben.zstd.ZstdInputStream; +import com.github.luben.zstd.ZstdOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.message.BasicHeader; +import org.junit.Test; + +public class CompressedStreamFactoryTest { + + private final CompressedStreamFactory factory = new CompressedStreamFactory(); + + @Test + public void testDetectContentEncodingAndGetInputStream_Gzip() throws Exception { + // Original data to compress and validate + String originalData = "Some data in GZIP"; + + // Creating encoding header + Header encodingHeader = new BasicHeader("Content-Encoding", "gzip"); + + // Creating a gzip byte array using GZIPOutputStream + byte[] gzipData; + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) { + gzipOutputStream.write(originalData.getBytes(StandardCharsets.UTF_8)); + gzipOutputStream.close(); // close to flush and finish the compression + gzipData = byteArrayOutputStream.toByteArray(); + } + + // Mocking input stream with the gzip data + InputStream gzipStream = new ByteArrayInputStream(gzipData); + + // Call the private method using reflection + InputStream resultStream = factory.createBasedOnEncodingHeader(gzipStream, encodingHeader); + + // Decompress and validate the data matches original + assertTrue(resultStream instanceof GZIPInputStream); + String decompressedData = IOUtils.toString(resultStream, StandardCharsets.UTF_8); + assertEquals(originalData, decompressedData); + } + + @Test + public void testDetectContentEncodingAndGetInputStream_Zstd() throws Exception { + // Original data to compress and validate + String originalData = "Some data in ZSTD"; + + // Creating encoding header + Header encodingHeader = new BasicHeader("Content-Encoding", "zstd"); + + // Creating a zstd byte array using ZstdOutputStream + byte[] zstdData; + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZstdOutputStream zstdOutputStream = new ZstdOutputStream(byteArrayOutputStream)) { + zstdOutputStream.write(originalData.getBytes(StandardCharsets.UTF_8)); + zstdOutputStream.close(); // close to flush and finish the compression + zstdData = byteArrayOutputStream.toByteArray(); + } + + // Mocking input stream with the zstd data + InputStream zstdStream = new ByteArrayInputStream(zstdData); + + // Call the private method using reflection + InputStream resultStream = factory.createBasedOnEncodingHeader(zstdStream, encodingHeader); + + // Decompress and validate the data matches original + assertTrue(resultStream instanceof ZstdInputStream); + String decompressedData = IOUtils.toString(resultStream, StandardCharsets.UTF_8); + assertEquals(originalData, decompressedData); + } +} diff --git a/thin_public_pom.xml b/thin_public_pom.xml index 08ab73da6..61118214c 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -64,6 +64,7 @@ UTF-8 2.0.13 1.6.9 + 1.5.6-5 @@ -267,6 +268,11 @@ jsoup ${jsoup.version} + + com.github.luben + zstd-jni + ${zstd-jni.version} + org.slf4j slf4j-api From bb4593e26e48ad1af9b6fd3a9fbff5161796cc6a Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:23:28 +0100 Subject: [PATCH 28/53] SNOW-1732054: Bump commons io dependency (#1942) --- parent-pom.xml | 2 +- thin_public_pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parent-pom.xml b/parent-pom.xml index 855d171db..c0c2dbb64 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -35,7 +35,7 @@ 1.2 1.17.0 1.4 - 2.11.0 + 2.17.0 1.2 1.5.4 0.9.5.4 diff --git a/thin_public_pom.xml b/thin_public_pom.xml index 61118214c..460ceaec2 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -39,7 +39,7 @@ 5.0.0 1.78.1 1.17.0 - 2.11.0 + 2.17.0 1.2 2.21.0 2.22.6 From 71440e55117e092593516ead921a3afb0f13479d Mon Sep 17 00:00:00 2001 From: Dawid Heyman Date: Wed, 30 Oct 2024 10:18:52 +0100 Subject: [PATCH 29/53] SNOW-1757822: Exclude directories from shaded packages check (#1944) --- FIPS/scripts/check_content.sh | 2 +- ci/scripts/check_content.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/FIPS/scripts/check_content.sh b/FIPS/scripts/check_content.sh index 565159073..a30eacec6 100755 --- a/FIPS/scripts/check_content.sh +++ b/FIPS/scripts/check_content.sh @@ -6,7 +6,7 @@ set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^com/github/" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then +if jar tvf $DIR/../target/snowflake-jdbc-fips.jar | awk '{print $8}' | grep -v -E "/$" | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^com/github/luben/zstd/" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi diff --git a/ci/scripts/check_content.sh b/ci/scripts/check_content.sh index 3a0747be2..1af33e56a 100755 --- a/ci/scripts/check_content.sh +++ b/ci/scripts/check_content.sh @@ -8,12 +8,12 @@ set -o pipefail DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^com/github/" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -v -E "/$" | grep -v -E "^(net|com)/snowflake" | grep -v -E "(com|net)/\$" | grep -v -E "^META-INF" | grep -v -E "^mozilla" | grep -v -E "^com/sun/jna" | grep -v com/sun/ | grep -v mime.types | grep -v -E "^com/github/luben/zstd/" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then echo "[ERROR] JDBC jar includes class not under the snowflake namespace" exit 1 fi -if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake" | grep -v -E "^META-INF/versions/.*/com/github" | grep -v -E "^aix/" | grep -v -E "^darwin/" | grep -v -E "^freebsd/" | grep -v -E "^linux/" | grep -v -E "^win/"; then +if jar tvf $DIR/../../target/snowflake-jdbc${package_modifier}.jar | awk '{print $8}' | grep -E "^META-INF/versions/.*.class" | grep -v -E "^META-INF/versions/.*/(net|com)/snowflake"; then echo "[ERROR] JDBC jar includes multi-release classes not under the snowflake namespace" exit 1 fi From c703b8de38e6fff8ca52c44ca885ddac760548ce Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:20:10 +0100 Subject: [PATCH 30/53] SNOW-1756807: Add note about GCP regional endpoints (#1943) --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index c2e296e95..a9d3cacb2 100644 --- a/README.rst +++ b/README.rst @@ -214,3 +214,8 @@ Support Feel free to file an issue or submit a PR here for general cases. For official support, contact Snowflake support at: https://community.snowflake.com/s/article/How-To-Submit-a-Support-Case-in-Snowflake-Lodge + +Note +---------- + +This driver currently does not support GCP regional endpoints. Please ensure that any workloads using through this driver do not require support for regional endpoints on GCP. If you have questions about this, please contact Snowflake Support. From 45afd71ac16ea55aeba91b8bacb4859344ae1b34 Mon Sep 17 00:00:00 2001 From: Piotr Bulawa Date: Wed, 30 Oct 2024 14:51:57 +0100 Subject: [PATCH 31/53] SNOW-1553930: Fix incorrect key size condition check for GCP and Azure (#1946) --- .../client/jdbc/cloud/storage/SnowflakeAzureClient.java | 2 +- .../snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClient.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClient.java index 4bec46ca7..0f8014ef6 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClient.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClient.java @@ -695,7 +695,7 @@ private SFPair createUploadStream( final InputStream stream; FileInputStream srcFileStream = null; try { - if (isEncrypting() && getEncryptionKeySize() < 256) { + if (isEncrypting() && getEncryptionKeySize() <= 256) { try { final InputStream uploadStream = uploadFromStream diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java index 003d894ae..31341c5a3 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java @@ -1036,7 +1036,7 @@ private SFPair createUploadStream( final InputStream stream; FileInputStream srcFileStream = null; try { - if (isEncrypting() && getEncryptionKeySize() < 256) { + if (isEncrypting() && getEncryptionKeySize() <= 256) { try { final InputStream uploadStream = uploadFromStream From 21e98c74145461212ff3a47f3540bc1fd3026a5e Mon Sep 17 00:00:00 2001 From: Piotr Bulawa Date: Wed, 30 Oct 2024 19:49:11 +0100 Subject: [PATCH 32/53] Bump version to 3.20.0 for release (#1947) --- CHANGELOG.rst | 4 ++++ FIPS/pom.xml | 4 ++-- parent-pom.xml | 2 +- pom.xml | 4 ++-- src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 72c788c36..76e948b1d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,7 @@ +**JDBC Driver 3.20.0** + +- \||Please Refer to Release Notes at https://docs.snowflake.com/en/release-notes/clients-drivers/jdbc + **JDBC Driver 3.19.1** - \||Please Refer to Release Notes at https://docs.snowflake.com/en/release-notes/clients-drivers/jdbc diff --git a/FIPS/pom.xml b/FIPS/pom.xml index cbc6132af..5a9048f2a 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -5,12 +5,12 @@ net.snowflake snowflake-jdbc-parent - 3.19.2-SNAPSHOT + 3.20.0 ../parent-pom.xml snowflake-jdbc-fips - 3.19.2-SNAPSHOT + 3.20.0 jar snowflake-jdbc-fips diff --git a/parent-pom.xml b/parent-pom.xml index c0c2dbb64..2617630fd 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -5,7 +5,7 @@ net.snowflake snowflake-jdbc-parent - 3.19.2-SNAPSHOT + 3.20.0 pom diff --git a/pom.xml b/pom.xml index c7542a1fe..8a90512ab 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ net.snowflake snowflake-jdbc-parent - 3.19.2-SNAPSHOT + 3.20.0 ./parent-pom.xml ${artifactId} - 3.19.2-SNAPSHOT + 3.20.0 jar ${artifactId} diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java index bd03ddd55..4e9b01b30 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java @@ -37,7 +37,7 @@ public class SnowflakeDriver implements Driver { static SnowflakeDriver INSTANCE; public static final Properties EMPTY_PROPERTIES = new Properties(); - public static String implementVersion = "3.19.2"; + public static String implementVersion = "3.20.0"; static int majorVersion = 0; static int minorVersion = 0; From 2b685e144b20de10bed4802c8b9bf543655d9111 Mon Sep 17 00:00:00 2001 From: Piotr Bulawa Date: Thu, 31 Oct 2024 08:31:38 +0100 Subject: [PATCH 33/53] Prepare next development version (#1948) --- FIPS/pom.xml | 4 ++-- parent-pom.xml | 2 +- pom.xml | 4 ++-- src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 5a9048f2a..ba1929bd2 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -5,12 +5,12 @@ net.snowflake snowflake-jdbc-parent - 3.20.0 + 3.20.1-SNAPSHOT ../parent-pom.xml snowflake-jdbc-fips - 3.20.0 + 3.20.1-SNAPSHOT jar snowflake-jdbc-fips diff --git a/parent-pom.xml b/parent-pom.xml index 2617630fd..7f165f376 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -5,7 +5,7 @@ net.snowflake snowflake-jdbc-parent - 3.20.0 + 3.20.1-SNAPSHOT pom diff --git a/pom.xml b/pom.xml index 8a90512ab..69ff5b183 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ net.snowflake snowflake-jdbc-parent - 3.20.0 + 3.20.1-SNAPSHOT ./parent-pom.xml ${artifactId} - 3.20.0 + 3.20.1-SNAPSHOT jar ${artifactId} diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java index 4e9b01b30..b385e04c2 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java @@ -37,7 +37,7 @@ public class SnowflakeDriver implements Driver { static SnowflakeDriver INSTANCE; public static final Properties EMPTY_PROPERTIES = new Properties(); - public static String implementVersion = "3.20.0"; + public static String implementVersion = "3.20.1"; static int majorVersion = 0; static int minorVersion = 0; From 58325fd04b6baedd71a4ac72cde575259ad59abf Mon Sep 17 00:00:00 2001 From: Jelena Furundzic <141762304+sfc-gh-ext-simba-jf@users.noreply.github.com> Date: Thu, 31 Oct 2024 01:55:08 -0700 Subject: [PATCH 34/53] SNOW-1213115: Move test categories for even distribution (#1735) --- .github/workflows/build-test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 190425de4..2607c5d46 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: runConfig: [ {cloud: 'AWS', javaVersion: '8'}, {cloud: 'GCP', javaVersion: '11'}, {cloud: 'AZURE', javaVersion: '17'}, {cloud: 'AWS', javaVersion: '21'}] - category: ['TestCategoryResultSet,TestCategoryOthers,TestCategoryLoader,TestCategoryDiagnostic', 'TestCategoryConnection,TestCategoryStatement', 'TestCategoryArrow,TestCategoryCore', 'TestCategoryFips'] + category: ['TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader', 'TestCategoryOthers', 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic', 'TestCategoryFips'] additionalMavenProfile: [''] steps: - uses: actions/checkout@v4 @@ -74,7 +74,7 @@ jobs: fail-fast: false matrix: runConfig: [ {cloud: 'AWS', javaVersion: '8'}, {cloud: 'GCP', javaVersion: '11'}, {cloud: 'AZURE', javaVersion: '17'}, {cloud: 'AWS', javaVersion: '21'}] - category: ['TestCategoryResultSet,TestCategoryOthers,TestCategoryLoader,TestCategoryDiagnostic', 'TestCategoryConnection,TestCategoryStatement', 'TestCategoryArrow,TestCategoryCore', 'TestCategoryFips'] + category: ['TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader', 'TestCategoryOthers', 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic', 'TestCategoryFips'] additionalMavenProfile: [''] steps: - uses: actions/checkout@v4 @@ -107,7 +107,7 @@ jobs: matrix: image: [ 'jdbc-centos7-openjdk8', 'jdbc-centos7-openjdk11', 'jdbc-centos7-openjdk17', 'jdbc-centos7-openjdk21' ] cloud: [ 'AWS', 'AZURE', 'GCP' ] - category: ['TestCategoryResultSet,TestCategoryOthers,TestCategoryLoader,TestCategoryDiagnostic', 'TestCategoryConnection,TestCategoryStatement', 'TestCategoryArrow,TestCategoryCore', 'TestCategoryFips'] + category: ['TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader', 'TestCategoryOthers', 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic', 'TestCategoryFips'] additionalMavenProfile: ['', '-Dthin-jar'] steps: - uses: actions/checkout@v1 @@ -129,7 +129,7 @@ jobs: matrix: image: [ 'jdbc-centos7-openjdk8' ] cloud: [ 'AWS' ] - category: ['TestCategoryResultSet,TestCategoryOthers', 'TestCategoryConnection,TestCategoryStatement', 'TestCategoryCore,TestCategoryLoader'] + category: ['TestCategoryOthers', 'TestCategoryConnection,TestCategoryStatement', 'TestCategoryCore,TestCategoryLoader,TestCategoryResultSet'] is_old_driver: ['true'] steps: - uses: actions/checkout@v1 From f1b5790f725a9ff4f799d74272fe9e1396e94647 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:27:09 +0100 Subject: [PATCH 35/53] SNOW-1778027: Fix stopwatch flaky test (#1949) --- src/test/java/net/snowflake/client/util/StopwatchTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/net/snowflake/client/util/StopwatchTest.java b/src/test/java/net/snowflake/client/util/StopwatchTest.java index 9e44ce18a..ed5f5d743 100644 --- a/src/test/java/net/snowflake/client/util/StopwatchTest.java +++ b/src/test/java/net/snowflake/client/util/StopwatchTest.java @@ -36,7 +36,7 @@ public void testGetMillisWhenStopped() throws InterruptedException { @Test public void testGetMillisWithoutStopping() throws InterruptedException { stopwatch.start(); - TimeUnit.MILLISECONDS.sleep(20); + TimeUnit.MILLISECONDS.sleep(100); assertThat( stopwatch.elapsedMillis(), allOf(greaterThanOrEqualTo(10L), lessThanOrEqualTo(500L))); } From e37f8149db57e67b61b6e0d2f94e0152d20d295f Mon Sep 17 00:00:00 2001 From: Antoni Stachowski Date: Thu, 31 Oct 2024 16:47:51 +0100 Subject: [PATCH 36/53] Added missing test categories to integration tests (#1937) --- .../client/jdbc/SnowflakeDriverIT.java | 3 + .../DatabaseMetaDataResultSetLatestIT.java | 3 + .../jdbc/FileUploaderExpandFileNamesTest.java | 13 ++++ .../client/jdbc/FileUploaderLatestIT.java | 2 +- ...oaderPrepIT.java => FileUploaderPrep.java} | 2 +- .../jdbc/FileUploaderSessionlessTest.java | 2 +- .../client/jdbc/MaxLobSizeLatestIT.java | 3 + .../SnowflakeChunkDownloaderLatestIT.java | 29 +++++++++ .../storage/SnowflakeAzureClientLatestIT.java | 10 ++- .../storage/SnowflakeS3ClientLatestIT.java | 3 + .../log/JDK14LoggerWithClientLatestIT.java | 61 ++++++++++++------- 11 files changed, 105 insertions(+), 26 deletions(-) rename src/test/java/net/snowflake/client/jdbc/{FileUploaderPrepIT.java => FileUploaderPrep.java} (99%) diff --git a/src/test/java/com/snowflake/client/jdbc/SnowflakeDriverIT.java b/src/test/java/com/snowflake/client/jdbc/SnowflakeDriverIT.java index 8cf16c6bd..d2a7246f8 100644 --- a/src/test/java/com/snowflake/client/jdbc/SnowflakeDriverIT.java +++ b/src/test/java/com/snowflake/client/jdbc/SnowflakeDriverIT.java @@ -5,8 +5,11 @@ import java.sql.Connection; import java.sql.SQLException; import net.snowflake.client.AbstractDriverIT; +import net.snowflake.client.category.TestCategoryConnection; import org.junit.Test; +import org.junit.experimental.categories.Category; +@Category(TestCategoryConnection.class) public class SnowflakeDriverIT extends AbstractDriverIT { @Test diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultSetLatestIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultSetLatestIT.java index 0549a087d..f69260a69 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultSetLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultSetLatestIT.java @@ -15,8 +15,11 @@ import java.sql.Types; import java.util.Arrays; import java.util.List; +import net.snowflake.client.category.TestCategoryResultSet; import org.junit.Test; +import org.junit.experimental.categories.Category; +@Category(TestCategoryResultSet.class) public class DatabaseMetaDataResultSetLatestIT extends BaseJDBCTest { @Test(expected = SnowflakeLoggedFeatureNotSupportedException.class) diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderExpandFileNamesTest.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderExpandFileNamesTest.java index a4426d449..c874b3e33 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderExpandFileNamesTest.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderExpandFileNamesTest.java @@ -40,6 +40,8 @@ public void testProcessFileNames() throws Exception { folder.newFile("TestFileB"); String folderName = folder.getRoot().getCanonicalPath(); + String originalUserDir = System.getProperty("user.dir"); + String originalUserHome = System.getProperty("user.home"); System.setProperty("user.dir", folderName); System.setProperty("user.home", folderName); @@ -58,6 +60,17 @@ public void testProcessFileNames() throws Exception { assertTrue(files.contains(folderName + File.separator + "TestFileC")); assertTrue(files.contains(folderName + File.separator + "TestFileD")); assertTrue(files.contains(folderName + File.separator + "TestFileE~")); + + if (originalUserHome != null) { + System.setProperty("user.home", originalUserHome); + } else { + System.clearProperty("user.home"); + } + if (originalUserDir != null) { + System.setProperty("user.dir", originalUserDir); + } else { + System.clearProperty("user.dir"); + } } @Test diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderLatestIT.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderLatestIT.java index 378234715..995832362 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderLatestIT.java @@ -53,7 +53,7 @@ /** Tests for SnowflakeFileTransferAgent that require an active connection */ @Category(TestCategoryOthers.class) -public class FileUploaderLatestIT extends FileUploaderPrepIT { +public class FileUploaderLatestIT extends FileUploaderPrep { private static final String OBJ_META_STAGE = "testObjMeta"; private ObjectMapper mapper = new ObjectMapper(); private static final String PUT_COMMAND = "put file:///dummy/path/file2.gz @testStage"; diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderPrepIT.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java similarity index 99% rename from src/test/java/net/snowflake/client/jdbc/FileUploaderPrepIT.java rename to src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java index 73ebec2bf..d8aa8143f 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderPrepIT.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java @@ -13,7 +13,7 @@ import org.junit.rules.TemporaryFolder; /** File uploader test prep reused by IT/connection tests and sessionless tests */ -abstract class FileUploaderPrepIT extends BaseJDBCTest { +abstract class FileUploaderPrep extends BaseJDBCTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); private ObjectMapper mapper = new ObjectMapper(); diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java index 822ce8e92..e23800e4e 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java @@ -17,7 +17,7 @@ import org.junit.Test; /** Tests for SnowflakeFileTransferAgent.expandFileNames. */ -public class FileUploaderSessionlessTest extends FileUploaderPrepIT { +public class FileUploaderSessionlessTest extends FileUploaderPrep { private ObjectMapper mapper = new ObjectMapper(); diff --git a/src/test/java/net/snowflake/client/jdbc/MaxLobSizeLatestIT.java b/src/test/java/net/snowflake/client/jdbc/MaxLobSizeLatestIT.java index 8962b8141..fd2957528 100644 --- a/src/test/java/net/snowflake/client/jdbc/MaxLobSizeLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/MaxLobSizeLatestIT.java @@ -11,10 +11,13 @@ import java.sql.Statement; import net.snowflake.client.ConditionalIgnoreRule; import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryStatement; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Test; +import org.junit.experimental.categories.Category; +@Category(TestCategoryStatement.class) public class MaxLobSizeLatestIT extends BaseJDBCTest { /** diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeChunkDownloaderLatestIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeChunkDownloaderLatestIT.java index b597c4dd0..7dca1028e 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeChunkDownloaderLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeChunkDownloaderLatestIT.java @@ -11,11 +11,40 @@ import java.sql.Statement; import java.util.List; import java.util.Properties; +import net.snowflake.client.category.TestCategoryCore; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.mockito.Mockito; +@Category(TestCategoryCore.class) public class SnowflakeChunkDownloaderLatestIT extends BaseJDBCTest { + private static String originalProxyHost; + private static String originalProxyPort; + private static String originalNonProxyHosts; + @BeforeClass + public static void setUp() throws Exception { + originalProxyHost = System.getProperty("https.proxyHost"); + originalProxyPort = System.getProperty("https.proxyPort"); + originalNonProxyHosts = System.getProperty("https.nonProxyHosts"); + } + + private static void restoreProperty(String key, String value) { + if (value != null) { + System.setProperty(key, value); + } else { + System.clearProperty(key); + } + } + + @AfterClass + public static void tearDown() throws Exception { + restoreProperty("https.proxyHost", originalProxyHost); + restoreProperty("https.proxyPort", originalProxyPort); + restoreProperty("https.nonProxyHosts", originalNonProxyHosts); + } /** * Tests that the chunk downloader uses the maxHttpRetries and doesn't enter and infinite loop of * retries. diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientLatestIT.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientLatestIT.java index 93539005a..05050b669 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientLatestIT.java @@ -2,14 +2,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.spy; +import com.amazonaws.services.kms.model.UnsupportedOperationException; import com.microsoft.azure.storage.blob.ListBlobItem; import java.sql.Connection; import java.sql.SQLException; +import java.util.ArrayList; import net.snowflake.client.ConditionalIgnoreRule; import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryOthers; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.SFStatement; import net.snowflake.client.jdbc.BaseJDBCTest; @@ -18,7 +22,9 @@ import net.snowflake.client.jdbc.SnowflakeSQLLoggedException; import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; import org.junit.Test; +import org.junit.experimental.categories.Category; +@Category(TestCategoryOthers.class) public class SnowflakeAzureClientLatestIT extends BaseJDBCTest { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) @@ -42,11 +48,11 @@ public void testAzureClientSetupInvalidEncryptionKeySize() throws SQLException { @Test public void testCloudExceptionTest() { - Iterable mockList = null; + Iterable mockList = new ArrayList<>(); AzureObjectSummariesIterator iterator = new AzureObjectSummariesIterator(mockList); AzureObjectSummariesIterator spyIterator = spy(iterator); UnsupportedOperationException ex = assertThrows(UnsupportedOperationException.class, () -> spyIterator.remove()); - assertEquals(ex.getMessage(), "remove() method not supported"); + assertTrue(ex.getMessage().startsWith("remove() method not supported")); } } diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientLatestIT.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientLatestIT.java index de241162f..d9e1821c2 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientLatestIT.java @@ -15,6 +15,7 @@ import java.util.Properties; import net.snowflake.client.ConditionalIgnoreRule; import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryOthers; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.SFStatement; import net.snowflake.client.jdbc.BaseJDBCTest; @@ -24,8 +25,10 @@ import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.mockito.Mockito; +@Category(TestCategoryOthers.class) public class SnowflakeS3ClientLatestIT extends BaseJDBCTest { @Test diff --git a/src/test/java/net/snowflake/client/log/JDK14LoggerWithClientLatestIT.java b/src/test/java/net/snowflake/client/log/JDK14LoggerWithClientLatestIT.java index 232da8451..5c11cdf22 100644 --- a/src/test/java/net/snowflake/client/log/JDK14LoggerWithClientLatestIT.java +++ b/src/test/java/net/snowflake/client/log/JDK14LoggerWithClientLatestIT.java @@ -18,18 +18,33 @@ import java.util.Properties; import java.util.logging.Level; import net.snowflake.client.AbstractDriverIT; +import net.snowflake.client.ConditionalIgnoreRule; +import net.snowflake.client.RunningOnWin; +import net.snowflake.client.category.TestCategoryOthers; import net.snowflake.client.jdbc.SnowflakeSQLLoggedException; import org.apache.commons.io.FileUtils; +import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TemporaryFolder; +@Category(TestCategoryOthers.class) public class JDK14LoggerWithClientLatestIT extends AbstractDriverIT { + @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + String homePath = systemGetProperty("user.home"); @Test - public void testJDK14LoggingWithClientConfig() { - Path configFilePath = Paths.get("config.json"); - String configJson = "{\"common\":{\"log_level\":\"debug\",\"log_path\":\"logs\"}}"; + @Ignore + public void testJDK14LoggingWithClientConfig() throws IOException { + File configFile = tmpFolder.newFile("config.json"); + Path configFilePath = configFile.toPath(); + File logFolder = tmpFolder.newFolder("logs"); + Path logFolderPath = logFolder.toPath(); + String configJson = + "{\"common\":{\"log_level\":\"debug\",\"log_path\":\"" + logFolderPath + "\"}}"; try { Files.write(configFilePath, configJson.getBytes()); Properties properties = new Properties(); @@ -38,11 +53,8 @@ public void testJDK14LoggingWithClientConfig() { Statement statement = connection.createStatement()) { statement.executeQuery("select 1"); - File file = new File("logs/jdbc/"); + File file = new File(Paths.get(logFolderPath.toString(), "jdbc").toString()); assertTrue(file.exists()); - - Files.deleteIfExists(configFilePath); - FileUtils.deleteDirectory(new File("logs")); } } catch (IOException e) { fail("testJDK14LoggingWithClientConfig failed"); @@ -62,11 +74,15 @@ public void testJDK14LoggingWithClientConfigInvalidConfigFilePath() throws SQLEx } @Test - public void testJDK14LoggingWithClientConfigPermissionError() throws IOException, SQLException { - Path configFilePath = Paths.get("config.json"); - String configJson = "{\"common\":{\"log_level\":\"debug\",\"log_path\":\"logs\"}}"; - Path directoryPath = Files.createDirectory(Paths.get("logs")); - File directory = directoryPath.toFile(); + @Ignore + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnWin.class) + public void testJDK14LoggingWithClientConfigPermissionError() throws IOException { + File configFile = tmpFolder.newFile("config.json"); + Path configFilePath = configFile.toPath(); + File directory = tmpFolder.newFolder("logs"); + Path directoryPath = directory.toPath(); + String configJson = + "{\"common\":{\"log_level\":\"debug\",\"log_path\":\"" + directoryPath + "\"}}"; HashSet perms = new HashSet<>(); perms.add(PosixFilePermission.OWNER_READ); perms.add(PosixFilePermission.GROUP_READ); @@ -77,9 +93,6 @@ public void testJDK14LoggingWithClientConfigPermissionError() throws IOException Properties properties = new Properties(); properties.put("client_config_file", configFilePath.toString()); assertThrows(SQLException.class, () -> getConnection(properties)); - - Files.delete(configFilePath); - directory.delete(); } @Test @@ -99,11 +112,15 @@ public void testJDK14LoggerWithQuotesInMessage() { } @Test + @Ignore public void testJDK14LoggingWithMissingLogPathClientConfig() throws Exception { - Path configFilePath = Paths.get("config.json"); + File configFile = tmpFolder.newFile("config.json"); + Path configFilePath = configFile.toPath(); String configJson = "{\"common\":{\"log_level\":\"debug\"}}"; + Path home = tmpFolder.getRoot().toPath(); + System.setProperty("user.home", home.toString()); - Path homeLogPath = Paths.get(homePath, "jdbc"); + Path homeLogPath = Paths.get(home.toString(), "jdbc"); Files.write(configFilePath, configJson.getBytes()); Properties properties = new Properties(); properties.put("client_config_file", configFilePath.toString()); @@ -119,21 +136,23 @@ public void testJDK14LoggingWithMissingLogPathClientConfig() throws Exception { Files.deleteIfExists(configFilePath); FileUtils.deleteDirectory(new File(homeLogPath.toString())); } + } finally { + System.setProperty("user.home", homePath); } } @Test + @Ignore public void testJDK14LoggingWithMissingLogPathNoHomeDirClientConfig() throws Exception { System.clearProperty("user.home"); - Path configFilePath = Paths.get("config.json"); + File configFile = tmpFolder.newFile("config.json"); + Path configFilePath = configFile.toPath(); String configJson = "{\"common\":{\"log_level\":\"debug\"}}"; Files.write(configFilePath, configJson.getBytes()); Properties properties = new Properties(); properties.put("client_config_file", configFilePath.toString()); - try (Connection connection = getConnection(properties); - Statement statement = connection.createStatement()) { - + try (Connection connection = getConnection(properties)) { fail("testJDK14LoggingWithMissingLogPathNoHomeDirClientConfig failed"); } catch (SnowflakeSQLLoggedException e) { // Succeed From e28c15eb26594dace0be91c55f46a1721a7f0a15 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:52:56 +0100 Subject: [PATCH 37/53] SNOW-1016464: Bump old driver version (#1934) --- TestOnly/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TestOnly/pom.xml b/TestOnly/pom.xml index 109b03c74..0b8143b5f 100644 --- a/TestOnly/pom.xml +++ b/TestOnly/pom.xml @@ -4,7 +4,7 @@ net.snowflake snowflake-jdbc-test - 3.9.2 + 3.13.21 snowflake-jdbc-test http://maven.apache.org From fd0ddd5afa153518bb1a1b60794bcb100e49c722 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Tue, 5 Nov 2024 07:25:09 +0100 Subject: [PATCH 38/53] SNOW-1786156: Replace raw asserts with exceptions (#1953) --- .../client/jdbc/SnowflakeFileTransferAgent.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java index 895275cef..ce289e90b 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java @@ -925,11 +925,13 @@ private void parseCommand() throws SnowflakeSQLException { // get source file locations as array (apply to both upload and download) JsonNode locationsNode = jsonNode.path("data").path("src_locations"); + if (!locationsNode.isArray()) { + throw new SnowflakeSQLException( + queryID, ErrorCode.INTERNAL_ERROR, "src_locations must be an array"); + } queryID = jsonNode.path("data").path("queryId").asText(); - assert locationsNode.isArray(); - String[] src_locations; try { @@ -1459,7 +1461,13 @@ public static List getFileTransferMetadatas( } // For UPLOAD we expect encryptionMaterial to have length 1 - assert encryptionMaterial.size() == 1; + if (encryptionMaterial.size() != 1) { + throw new SnowflakeSQLException( + queryId, + ErrorCode.INTERNAL_ERROR, + "Encryption material for UPLOAD should have size 1 but have " + + encryptionMaterial.size()); + } final Set sourceFiles = expandFileNames(srcLocations, queryId); From d13c63ca92f4daf61c0bd1f054b3931ccdaca253 Mon Sep 17 00:00:00 2001 From: Piotr Fus Date: Wed, 6 Nov 2024 08:09:00 +0100 Subject: [PATCH 39/53] SNOW-1786192 Use 12 bytes as IV length for GCM (#1955) --- .../cloud/storage/GcmEncryptionProvider.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/GcmEncryptionProvider.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/GcmEncryptionProvider.java index fa31a6a0c..c3f53c0ea 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/GcmEncryptionProvider.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/GcmEncryptionProvider.java @@ -30,7 +30,8 @@ import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; class GcmEncryptionProvider { - private static final int TAG_LENGTH = 128; + private static final int TAG_LENGTH_IN_BITS = 128; + private static final int IV_LENGTH_IN_BYTES = 12; private static final String AES = "AES"; private static final String FILE_CIPHER = "AES/GCM/NoPadding"; private static final String KEY_CIPHER = "AES/GCM/NoPadding"; @@ -64,8 +65,8 @@ static InputStream encrypt( byte[] kek = base64Decoder.decode(encMat.getQueryStageMasterKey()); int keySize = kek.length; byte[] keyBytes = new byte[keySize]; - byte[] dataIvBytes = new byte[blockSize]; - byte[] keyIvBytes = new byte[blockSize]; + byte[] dataIvBytes = new byte[IV_LENGTH_IN_BYTES]; + byte[] keyIvBytes = new byte[IV_LENGTH_IN_BYTES]; initRandomIvsAndFileKey(dataIvBytes, keyIvBytes, keyBytes); byte[] encryptedKey = encryptKey(kek, keyBytes, keyIvBytes, keyAad); CipherInputStream cis = encryptContent(src, keyBytes, dataIvBytes, dataAad); @@ -94,7 +95,7 @@ private static byte[] encryptKey(byte[] kekBytes, byte[] keyBytes, byte[] keyIvD throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, NoSuchAlgorithmException { SecretKey kek = new SecretKeySpec(kekBytes, 0, kekBytes.length, AES); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, keyIvData); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH_IN_BITS, keyIvData); Cipher keyCipher = Cipher.getInstance(KEY_CIPHER); keyCipher.init(Cipher.ENCRYPT_MODE, kek, gcmParameterSpec); if (aad != null) { @@ -108,7 +109,7 @@ private static CipherInputStream encryptContent( throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException { SecretKey fileKey = new SecretKeySpec(keyBytes, 0, keyBytes.length, AES); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, dataIvBytes); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH_IN_BITS, dataIvBytes); Cipher fileCipher = Cipher.getInstance(FILE_CIPHER); fileCipher.init(Cipher.ENCRYPT_MODE, fileKey, gcmParameterSpec); if (aad != null) { @@ -180,7 +181,7 @@ private static CipherInputStream decryptContentFromStream( InputStream inputStream, byte[] ivBytes, byte[] fileKeyBytes, byte[] aad) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException { - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, ivBytes); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH_IN_BITS, ivBytes); SecretKey fileKey = new SecretKeySpec(fileKeyBytes, AES); Cipher fileCipher = Cipher.getInstance(FILE_CIPHER); fileCipher.init(Cipher.DECRYPT_MODE, fileKey, gcmParameterSpec); @@ -195,7 +196,7 @@ private static void decryptContentFromFile( throws InvalidKeyException, InvalidAlgorithmParameterException, IOException, NoSuchPaddingException, NoSuchAlgorithmException { SecretKey fileKey = new SecretKeySpec(fileKeyBytes, AES); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, cekIvBytes); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH_IN_BITS, cekIvBytes); byte[] buffer = new byte[BUFFER_SIZE]; Cipher fileCipher = Cipher.getInstance(FILE_CIPHER); fileCipher.init(Cipher.DECRYPT_MODE, fileKey, gcmParameterSpec); @@ -224,7 +225,7 @@ private static byte[] decryptKey(byte[] kekBytes, byte[] ivBytes, byte[] keyByte throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, NoSuchAlgorithmException { SecretKey kek = new SecretKeySpec(kekBytes, 0, kekBytes.length, AES); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, ivBytes); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH_IN_BITS, ivBytes); Cipher keyCipher = Cipher.getInstance(KEY_CIPHER); keyCipher.init(Cipher.DECRYPT_MODE, kek, gcmParameterSpec); if (aad != null) { From 3aac6be4d68c80335a81f89c9604ff26125bf86b Mon Sep 17 00:00:00 2001 From: Jelena Furundzic <141762304+sfc-gh-ext-simba-jf@users.noreply.github.com> Date: Wed, 6 Nov 2024 01:17:28 -0800 Subject: [PATCH 40/53] SNOW-1016469: Adding missing comments to public methods (#1928) --- .../client/config/SFClientConfigParser.java | 1 + .../client/core/ChunkDownloader.java | 3 + .../client/core/CredentialManager.java | 2 +- .../client/core/DataConversionContext.java | 31 +- .../net/snowflake/client/core/EventUtil.java | 2 +- .../client/core/HeartbeatBackground.java | 1 + .../client/core/HttpClientSettingsKey.java | 6 +- .../net/snowflake/client/core/HttpUtil.java | 6 +- .../client/core/PrivateLinkDetector.java | 3 + .../client/core/QueryContextCache.java | 4 + .../snowflake/client/core/QueryStatus.java | 22 ++ .../net/snowflake/client/core/ResultUtil.java | 6 + .../client/core/SFArrowResultSet.java | 3 +- .../snowflake/client/core/SFBaseSession.java | 337 +++++++++++++++++- .../client/core/SFBaseStatement.java | 19 +- .../snowflake/client/core/SFException.java | 54 ++- .../client/core/SFJsonResultSet.java | 2 +- .../snowflake/client/core/SFResultSet.java | 6 +- .../net/snowflake/client/core/SFSession.java | 4 +- .../net/snowflake/client/core/SFSqlInput.java | 1 + .../snowflake/client/core/SFStatement.java | 4 + .../snowflake/client/core/SessionUtil.java | 14 +- .../SnowflakeMutableProxyRoutePlanner.java | 18 + .../net/snowflake/client/core/StmtUtil.java | 5 +- .../arrow/AbstractArrowVectorConverter.java | 14 + .../client/core/arrow/ArrayConverter.java | 6 + .../arrow/ArrowResultChunkIndexSorter.java | 2 +- .../client/core/arrow/ArrowResultUtil.java | 44 +-- .../core/arrow/ArrowVectorConverter.java | 4 +- .../core/arrow/ArrowVectorConverterUtil.java | 3 +- .../core/arrow/BigIntToFixedConverter.java | 5 + .../core/arrow/BigIntToTimeConverter.java | 15 + .../arrow/BigIntToTimestampLTZConverter.java | 7 +- .../arrow/BigIntToTimestampNTZConverter.java | 5 + .../core/arrow/BitToBooleanConverter.java | 5 + .../client/core/arrow/DateConverter.java | 6 + .../arrow/DecimalToScaledFixedConverter.java | 5 + .../core/arrow/DoubleToRealConverter.java | 6 + .../core/arrow/IntToFixedConverter.java | 5 + .../client/core/arrow/IntToTimeConverter.java | 6 + .../client/core/arrow/MapConverter.java | 6 + .../core/arrow/SmallIntToFixedConverter.java | 5 + ...hreeFieldStructToTimestampTZConverter.java | 5 + .../core/arrow/TinyIntToFixedConverter.java | 5 + ...TwoFieldStructToTimestampLTZConverter.java | 5 + ...TwoFieldStructToTimestampNTZConverter.java | 5 + .../arrow/VarBinaryToBinaryConverter.java | 6 + .../client/core/arrow/VarCharConverter.java | 5 + .../core/arrow/VectorTypeConverter.java | 6 + .../client/core/bind/BindUploader.java | 13 +- .../client/jdbc/ArrowResultChunk.java | 13 +- .../jdbc/DefaultSFConnectionHandler.java | 1 + .../snowflake/client/jdbc/QueryStatusV2.java | 6 +- .../snowflake/client/jdbc/RestRequest.java | 1 + .../client/jdbc/ResultJsonParserV2.java | 8 + .../client/jdbc/SFConnectionHandler.java | 44 ++- .../client/jdbc/SnowflakeBaseResultSet.java | 19 +- .../client/jdbc/SnowflakeChunkDownloader.java | 1 + .../client/jdbc/SnowflakeColumn.java | 20 +- .../client/jdbc/SnowflakeColumnMetadata.java | 13 + .../client/jdbc/SnowflakeConnection.java | 14 +- .../client/jdbc/SnowflakeConnectionV1.java | 10 +- .../client/jdbc/SnowflakeDriver.java | 4 +- .../jdbc/SnowflakeFileTransferAgent.java | 6 +- .../jdbc/SnowflakeFileTransferConfig.java | 10 +- .../jdbc/SnowflakePreparedStatement.java | 17 +- .../client/jdbc/SnowflakeResultSet.java | 7 +- .../jdbc/SnowflakeResultSetSerializable.java | 6 + .../SnowflakeResultSetSerializableV1.java | 7 + .../client/jdbc/SnowflakeResultSetV1.java | 6 +- .../SnowflakeRichResultSetSerializableV1.java | 6 + .../client/jdbc/SnowflakeSQLException.java | 77 +++- .../jdbc/SnowflakeSQLLoggedException.java | 100 +++++- .../client/jdbc/SnowflakeStatement.java | 12 +- .../snowflake/client/jdbc/SnowflakeType.java | 7 +- .../snowflake/client/jdbc/SnowflakeUtil.java | 35 +- .../cloud/storage/EncryptionProvider.java | 17 +- .../client/jdbc/cloud/storage/S3HttpUtil.java | 2 +- .../cloud/storage/SnowflakeGCSClient.java | 6 +- .../jdbc/cloud/storage/SnowflakeS3Client.java | 19 +- .../cloud/storage/SnowflakeStorageClient.java | 14 +- .../cloud/storage/StorageClientFactory.java | 1 + .../jdbc/telemetryOOB/TelemetryService.java | 59 ++- .../net/snowflake/client/log/ArgSupplier.java | 5 + .../net/snowflake/client/log/JDK14Logger.java | 7 +- .../net/snowflake/client/log/SFLogLevel.java | 4 +- .../snowflake/client/util/SecretDetector.java | 3 +- .../client/util/TimeMeasurement.java | 12 +- 88 files changed, 1186 insertions(+), 166 deletions(-) diff --git a/src/main/java/net/snowflake/client/config/SFClientConfigParser.java b/src/main/java/net/snowflake/client/config/SFClientConfigParser.java index a0ca0fa11..45b38dbfa 100644 --- a/src/main/java/net/snowflake/client/config/SFClientConfigParser.java +++ b/src/main/java/net/snowflake/client/config/SFClientConfigParser.java @@ -33,6 +33,7 @@ public class SFClientConfigParser { * @param configFilePath SF_CLIENT_CONFIG_FILE parameter read from connection URL or connection * properties * @return SFClientConfig + * @throws IOException if exception encountered when reading config file. */ public static SFClientConfig loadSFClientConfig(String configFilePath) throws IOException { if (configFilePath != null) { diff --git a/src/main/java/net/snowflake/client/core/ChunkDownloader.java b/src/main/java/net/snowflake/client/core/ChunkDownloader.java index 8818c9c17..e881eb9a1 100644 --- a/src/main/java/net/snowflake/client/core/ChunkDownloader.java +++ b/src/main/java/net/snowflake/client/core/ChunkDownloader.java @@ -14,6 +14,8 @@ public interface ChunkDownloader { * be blocked if the chunk is not ready to be consumed (a.k.a not loaded into memory yet) * * @return result chunk with data loaded + * @throws InterruptedException if downloading thread was interrupted + * @throws SnowflakeSQLException if downloader encountered an error */ SnowflakeResultChunk getNextChunkToConsume() throws InterruptedException, SnowflakeSQLException; @@ -21,6 +23,7 @@ public interface ChunkDownloader { * Terminate the chunk downloader, release all resources allocated * * @return metrics measuring downloader performance + * @throws InterruptedException if error encountered */ DownloaderMetrics terminate() throws InterruptedException; } diff --git a/src/main/java/net/snowflake/client/core/CredentialManager.java b/src/main/java/net/snowflake/client/core/CredentialManager.java index a5b919d3d..08e9e6b9a 100644 --- a/src/main/java/net/snowflake/client/core/CredentialManager.java +++ b/src/main/java/net/snowflake/client/core/CredentialManager.java @@ -47,7 +47,7 @@ void resetSecureStorageManager() { /** * Testing purpose. Inject a mock manager. * - * @param manager + * @param manager SecureStorageManager */ void injectSecureStorageManager(SecureStorageManager manager) { logger.debug("Injecting secure storage manager"); diff --git a/src/main/java/net/snowflake/client/core/DataConversionContext.java b/src/main/java/net/snowflake/client/core/DataConversionContext.java index 86bba8208..d0f80e021 100644 --- a/src/main/java/net/snowflake/client/core/DataConversionContext.java +++ b/src/main/java/net/snowflake/client/core/DataConversionContext.java @@ -12,25 +12,42 @@ * to a single result set. a.k.a each result set object should have its own formatter info */ public interface DataConversionContext { - /** timestamp_ltz formatter */ + /** + * @return timestamp_ltz formatter + */ SnowflakeDateTimeFormat getTimestampLTZFormatter(); - /** timestamp_ntz formatter */ + /** + * @return timestamp_ntz formatter + */ SnowflakeDateTimeFormat getTimestampNTZFormatter(); - /** timestamp_tz formatter */ + /** + * @return timestamp_ntz formatter + */ SnowflakeDateTimeFormat getTimestampTZFormatter(); - /** date formatter */ + /** + * @return date formatter + */ SnowflakeDateTimeFormat getDateFormatter(); - /** time formatter */ + /** + * @return time formatter + */ SnowflakeDateTimeFormat getTimeFormatter(); - /** binary formatter */ + /** + * @return binary formatter + */ SFBinaryFormat getBinaryFormatter(); - /** get scale from Snowflake metadata */ + /** + * get scale from Snowflake metadata + * + * @param columnIndex column index + * @return scale value + */ int getScale(int columnIndex); /** diff --git a/src/main/java/net/snowflake/client/core/EventUtil.java b/src/main/java/net/snowflake/client/core/EventUtil.java index d45cd0676..ed25c5988 100644 --- a/src/main/java/net/snowflake/client/core/EventUtil.java +++ b/src/main/java/net/snowflake/client/core/EventUtil.java @@ -36,7 +36,7 @@ public class EventUtil { /** * Junit is not recognizing the system properties for EventTest, so overriding the value here * - * @param value + * @param value string value */ public static void setDumpPathPrefixForTesting(String value) { DUMP_PATH_PREFIX = value; diff --git a/src/main/java/net/snowflake/client/core/HeartbeatBackground.java b/src/main/java/net/snowflake/client/core/HeartbeatBackground.java index 25ba5f946..6942a9e5a 100644 --- a/src/main/java/net/snowflake/client/core/HeartbeatBackground.java +++ b/src/main/java/net/snowflake/client/core/HeartbeatBackground.java @@ -67,6 +67,7 @@ private HeartbeatBackground() {} * @param session the session will be added * @param masterTokenValidityInSecs time interval for which client need to check validity of * master token with server + * @param heartbeatFrequencyInSecs heartbeat frequency in seconds */ protected synchronized void addSession( SFSession session, long masterTokenValidityInSecs, int heartbeatFrequencyInSecs) { diff --git a/src/main/java/net/snowflake/client/core/HttpClientSettingsKey.java b/src/main/java/net/snowflake/client/core/HttpClientSettingsKey.java index f65b9e29d..d3a356e5a 100644 --- a/src/main/java/net/snowflake/client/core/HttpClientSettingsKey.java +++ b/src/main/java/net/snowflake/client/core/HttpClientSettingsKey.java @@ -122,7 +122,11 @@ public String getUserAgentSuffix() { return this.userAgentSuffix; } - /** Be careful of using this! Should only be called when password is later masked. */ + /** + * Be careful of using this! Should only be called when password is later masked. + * + * @return proxy password + */ @SnowflakeJdbcInternalApi public String getProxyPassword() { return this.proxyPassword; diff --git a/src/main/java/net/snowflake/client/core/HttpUtil.java b/src/main/java/net/snowflake/client/core/HttpUtil.java index 166bd7e0a..23b83df09 100644 --- a/src/main/java/net/snowflake/client/core/HttpUtil.java +++ b/src/main/java/net/snowflake/client/core/HttpUtil.java @@ -65,6 +65,7 @@ import org.apache.http.ssl.SSLInitializationException; import org.apache.http.util.EntityUtils; +/** HttpUtil class */ public class HttpUtil { private static final SFLogger logger = SFLoggerFactory.getLogger(HttpUtil.class); @@ -168,7 +169,7 @@ public static void setProxyForS3(HttpClientSettingsKey key, ClientConfiguration * * @param proxyProperties proxy properties * @param clientConfig the configuration needed by S3 to set the proxy - * @throws SnowflakeSQLException + * @throws SnowflakeSQLException when exception encountered * @deprecated Use {@link S3HttpUtil#setSessionlessProxyForS3(Properties, ClientConfiguration)} * instead */ @@ -184,7 +185,7 @@ public static void setSessionlessProxyForS3( * * @param proxyProperties proxy properties * @param opContext the configuration needed by Azure to set the proxy - * @throws SnowflakeSQLException + * @throws SnowflakeSQLException when invalid proxy properties encountered */ public static void setSessionlessProxyForAzure( Properties proxyProperties, OperationContext opContext) throws SnowflakeSQLException { @@ -723,6 +724,7 @@ public static String executeGeneralRequest( * @param includeRetryParameters whether to include retry parameters in retried requests * @param retryOnHTTP403 whether to retry on HTTP 403 or not * @param ocspAndProxyKey OCSP mode and proxy settings for httpclient + * @param execTimeData query execution time telemetry data object * @return response * @throws SnowflakeSQLException if Snowflake error occurs * @throws IOException raises if a general IO error occurs diff --git a/src/main/java/net/snowflake/client/core/PrivateLinkDetector.java b/src/main/java/net/snowflake/client/core/PrivateLinkDetector.java index 8d4a01742..e60f6859d 100644 --- a/src/main/java/net/snowflake/client/core/PrivateLinkDetector.java +++ b/src/main/java/net/snowflake/client/core/PrivateLinkDetector.java @@ -6,6 +6,9 @@ public class PrivateLinkDetector { * We can only tell if private link is enabled for certain hosts when the hostname contains the * word 'privatelink' but we don't have a good way of telling if a private link connection is * expected for internal stages for example. + * + * @param host host + * @return true if host is considered as privatelink environment */ public static boolean isPrivateLink(String host) { return host.toLowerCase().contains(".privatelink.snowflakecomputing."); diff --git a/src/main/java/net/snowflake/client/core/QueryContextCache.java b/src/main/java/net/snowflake/client/core/QueryContextCache.java index 85fde42ac..60cd8501a 100644 --- a/src/main/java/net/snowflake/client/core/QueryContextCache.java +++ b/src/main/java/net/snowflake/client/core/QueryContextCache.java @@ -274,6 +274,8 @@ private static QueryContextElement deserializeQueryContextElement(JsonNode node) * Deserialize the QueryContext cache from a QueryContextDTO object. This function currently is * only used in QueryContextCacheTest.java where we check that after serialization and * deserialization, the cache is the same as before. + * + * @param queryContextDTO QueryContextDTO to deserialize. */ public void deserializeQueryContextDTO(QueryContextDTO queryContextDTO) { synchronized (this) { @@ -335,6 +337,8 @@ private static QueryContextElement deserializeQueryContextElementDTO( /** * Serialize the QueryContext cache to a QueryContextDTO object, which can be serialized to JSON * automatically later. + * + * @return {@link QueryContextDTO} */ public QueryContextDTO serializeQueryContextDTO() { synchronized (this) { diff --git a/src/main/java/net/snowflake/client/core/QueryStatus.java b/src/main/java/net/snowflake/client/core/QueryStatus.java index bc16abf62..792f4b538 100644 --- a/src/main/java/net/snowflake/client/core/QueryStatus.java +++ b/src/main/java/net/snowflake/client/core/QueryStatus.java @@ -39,6 +39,7 @@ public String getDescription() { /** * @deprecated use {@link net.snowflake.client.jdbc.QueryStatusV2} instead + * @return error message */ @Deprecated public String getErrorMessage() { @@ -47,6 +48,7 @@ public String getErrorMessage() { /** * @deprecated use {@link net.snowflake.client.jdbc.QueryStatusV2} instead + * @return error code */ @Deprecated public int getErrorCode() { @@ -55,6 +57,7 @@ public int getErrorCode() { /** * @deprecated use {@link net.snowflake.client.jdbc.QueryStatusV2} instead + * @param message the error message */ @Deprecated public void setErrorMessage(String message) { @@ -63,12 +66,19 @@ public void setErrorMessage(String message) { /** * @deprecated use {@link net.snowflake.client.jdbc.QueryStatusV2} instead + * @param errorCode the error code */ @Deprecated public void setErrorCode(int errorCode) { this.errorCode = errorCode; } + /** + * Check if query is still running. + * + * @param status QueryStatus + * @return true if query is still running + */ public static boolean isStillRunning(QueryStatus status) { switch (status.getValue()) { case 0: // "RUNNING" @@ -83,6 +93,12 @@ public static boolean isStillRunning(QueryStatus status) { } } + /** + * Check if query status is an error + * + * @param status QueryStatus + * @return true if query status is an error status + */ public static boolean isAnError(QueryStatus status) { switch (status.getValue()) { case 1: // Aborting @@ -97,6 +113,12 @@ public static boolean isAnError(QueryStatus status) { } } + /** + * Get the query status from a string description + * + * @param description the status description + * @return QueryStatus + */ public static QueryStatus getStatusFromString(String description) { if (description != null) { for (QueryStatus st : QueryStatus.values()) { diff --git a/src/main/java/net/snowflake/client/core/ResultUtil.java b/src/main/java/net/snowflake/client/core/ResultUtil.java index b894f4259..20acee866 100644 --- a/src/main/java/net/snowflake/client/core/ResultUtil.java +++ b/src/main/java/net/snowflake/client/core/ResultUtil.java @@ -83,6 +83,12 @@ public static Object effectiveParamValue(Map parameters, String /** * Helper function building a formatter for a specialized timestamp type. Note that it will be * based on either the 'param' value if set, or the default format provided. + * + * @param parameters keyed in parameter name and valued in parameter value + * @param id id + * @param param timestamp output format param + * @param defaultFormat default format + * @return {@link SnowflakeDateTimeFormat} */ public static SnowflakeDateTimeFormat specializedFormatter( Map parameters, String id, String param, String defaultFormat) { diff --git a/src/main/java/net/snowflake/client/core/SFArrowResultSet.java b/src/main/java/net/snowflake/client/core/SFArrowResultSet.java index 14f21e8d1..cbb92bcde 100644 --- a/src/main/java/net/snowflake/client/core/SFArrowResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFArrowResultSet.java @@ -178,7 +178,8 @@ public SFArrowResultSet( * * @param resultSetSerializable data returned in query response * @param telemetryClient telemetryClient - * @throws SQLException + * @param sortResult set if results should be sorted + * @throws SQLException if exception encountered */ public SFArrowResultSet( SnowflakeResultSetSerializableV1 resultSetSerializable, diff --git a/src/main/java/net/snowflake/client/core/SFBaseSession.java b/src/main/java/net/snowflake/client/core/SFBaseSession.java index 382dcb877..9222b4a57 100644 --- a/src/main/java/net/snowflake/client/core/SFBaseSession.java +++ b/src/main/java/net/snowflake/client/core/SFBaseSession.java @@ -162,6 +162,8 @@ public long getMemoryLimitForTesting() { * Part of the JDBC API, where client applications may fetch a Map of Properties to set various * attributes. This is not used internally by any driver component, but should be maintained by * the Session object. + * + * @return client info as Properties */ public Properties getClientInfo() { // defensive copy to avoid client from changing the properties @@ -171,10 +173,20 @@ public Properties getClientInfo() { return copy; } + /** + * Set common parameters + * + * @param parameters the parameters to set + */ public void setCommonParameters(Map parameters) { this.commonParameters = parameters; } + /** + * Get common parameters + * + * @return Map of common parameters + */ public Map getCommonParameters() { return this.commonParameters; } @@ -183,12 +195,17 @@ public Map getCommonParameters() { * Gets the Property associated with the key 'name' in the ClientInfo map. * * @param name The key from which to fetch the Property. + * @return The ClientInfo entry property. */ public String getClientInfo(String name) { return this.clientInfo.getProperty(name); } - /** Returns a unique id for this session. */ + /** + * Returns a unique id for this session. + * + * @return unique id for session + */ public String getSessionId() { return sessionId; } @@ -202,86 +219,200 @@ public void setSessionId(String sessionId) { this.sessionId = sessionId; } + /** + * @return true if session is in SQLMode + */ public boolean isSfSQLMode() { return sfSQLMode; } + /** + * Set sfSQLMode + * + * @param sfSQLMode boolean + */ public void setSfSQLMode(boolean sfSQLMode) { this.sfSQLMode = sfSQLMode; } + /** + * Get the database version + * + * @return database version + */ public String getDatabaseVersion() { return databaseVersion; } + /** + * Set database version + * + * @param databaseVersion the version to set + */ public void setDatabaseVersion(String databaseVersion) { this.databaseVersion = databaseVersion; } + /** + * Get databse major version + * + * @return the database major version + */ public int getDatabaseMajorVersion() { return databaseMajorVersion; } + /** + * Set database major version + * + * @param databaseMajorVersion the database major version + */ public void setDatabaseMajorVersion(int databaseMajorVersion) { this.databaseMajorVersion = databaseMajorVersion; } + /** + * Get the database minor version + * + * @return database minor version + */ public int getDatabaseMinorVersion() { return databaseMinorVersion; } + /** + * Set the database minor version + * + * @param databaseMinorVersion the minor version + */ public void setDatabaseMinorVersion(int databaseMinorVersion) { this.databaseMinorVersion = databaseMinorVersion; } + /** + * Gets the value of CLIENT_ENABLE_LOG_INFO_STATEMENT_PARAMETERS if one has been set. False by + * default. + * + * @see CLIENT_ENABLE_LOG_INFO_STATEMENT_PARAMETERS + * @return true if enabled + */ public boolean getPreparedStatementLogging() { return this.preparedStatementLogging; } + /** + * Set prepared statement logging + * + * @see SFBaseSession#getPreparedStatementLogging() + * @param value boolean + */ public void setPreparedStatementLogging(boolean value) { this.preparedStatementLogging = value; } + /** + * Get inject file upload failure. Note: Should only be used in internal tests! + * + * @return file to fail + */ public String getInjectFileUploadFailure() { return this.injectFileUploadFailure; } + /** + * Set inject file upload failure Note: Should only be used in internal tests! + * + * @param fileToFail the file to fail + */ public void setInjectFileUploadFailure(String fileToFail) { this.injectFileUploadFailure = fileToFail; } + /** + * Get timestamp mapped type + * + * @see CLIENT_TIMESTAMP_TYPE_MAPPING + * @return {@link SnowflakeType} + */ public SnowflakeType getTimestampMappedType() { return timestampMappedType; } + /** + * Set the timestamp mapped type + * + * @see SFBaseSession#getTimestampMappedType() + * @param timestampMappedType SnowflakeType + */ public void setTimestampMappedType(SnowflakeType timestampMappedType) { this.timestampMappedType = timestampMappedType; } + /** + * Get if result column is case-insensitive + * + * @see SFBaseSession#setResultColumnCaseInsensitive(boolean) + * @return true if result column is case-insensitive + */ public boolean isResultColumnCaseInsensitive() { return isResultColumnCaseInsensitive; } + /** + * Set if result column is case-insensitive + * + * @see CLIENT_RESULT_COLUMN_CASE_INSENSITIVE + * @param resultColumnCaseInsensitive boolean + */ public void setResultColumnCaseInsensitive(boolean resultColumnCaseInsensitive) { isResultColumnCaseInsensitive = resultColumnCaseInsensitive; } + /** + * Check if we want to treat decimal as int JDBC types + * + * @see JDBC_TREAT_DECIMAL_AS_INT + * @return true if decimal is treated as int + */ public boolean isJdbcTreatDecimalAsInt() { return isJdbcTreatDecimalAsInt; } + /** + * Set if decimal should be treated as int type + * + * @see SFBaseSession#isJdbcTreatDecimalAsInt() + * @param jdbcTreatDecimalAsInt boolean + */ public void setJdbcTreatDecimalAsInt(boolean jdbcTreatDecimalAsInt) { isJdbcTreatDecimalAsInt = jdbcTreatDecimalAsInt; } + /** + * @return true if decimal should be treated as int for arrow types + */ public boolean isJdbcArrowTreatDecimalAsInt() { return isJdbcArrowTreatDecimalAsInt; } + /** + * Set if decimal should be treated as int for arrow types + * + * @param jdbcArrowTreatDecimalAsInt boolean + */ public void setJdbcArrowTreatDecimalAsInt(boolean jdbcArrowTreatDecimalAsInt) { isJdbcArrowTreatDecimalAsInt = jdbcArrowTreatDecimalAsInt; } + /** + * Get the server url + * + * @return the server url or null if it is not set + */ public String getServerUrl() { if (connectionPropertiesMap.containsKey(SFSessionProperty.SERVER_URL)) { return (String) connectionPropertiesMap.get(SFSessionProperty.SERVER_URL); @@ -289,6 +420,11 @@ public String getServerUrl() { return null; } + /** + * Get whether columns strings are quoted. + * + * @return value of 'stringsQuotedForColumnDef' connection property or false if not set. + */ public boolean isStringQuoted() { if (connectionPropertiesMap.containsKey(SFSessionProperty.STRINGS_QUOTED)) { return (Boolean) connectionPropertiesMap.get(SFSessionProperty.STRINGS_QUOTED); @@ -346,10 +482,21 @@ public void addProperty(String propertyName, Object propertyValue) throws SFExce } } + /** + * Get the connection properties map + * + * @return the connection properties map + */ public Map getConnectionPropertiesMap() { return connectionPropertiesMap; } + /** + * Get the http client key + * + * @return HttpClientSettingsKey + * @throws SnowflakeSQLException if exception encountered + */ public HttpClientSettingsKey getHttpClientKey() throws SnowflakeSQLException { // if key is already created, return it without making a new one if (ocspAndProxyAndGzipKey != null) { @@ -547,6 +694,7 @@ private void logHttpClientInitInfo(HttpClientSettingsKey key) { } } + /** Unset invalid proxy host and port values. */ public void unsetInvalidProxyHostAndPort() { // If proxyHost and proxyPort are used without http or https unset them, so they are not used // later by the ProxySelector. @@ -558,6 +706,11 @@ public void unsetInvalidProxyHostAndPort() { } } + /** + * Get OCSP mode + * + * @return {@link OCSPMode} + */ public OCSPMode getOCSPMode() { OCSPMode ret; @@ -576,18 +729,38 @@ public OCSPMode getOCSPMode() { return ret; } + /** + * Get the query timeout + * + * @return the query timeout value + */ public Integer getQueryTimeout() { return (Integer) this.connectionPropertiesMap.get(SFSessionProperty.QUERY_TIMEOUT); } + /** + * Get the user name + * + * @return user name + */ public String getUser() { return (String) this.connectionPropertiesMap.get(SFSessionProperty.USER); } + /** + * Get the server URL + * + * @return the server URL + */ public String getUrl() { return (String) this.connectionPropertiesMap.get(SFSessionProperty.SERVER_URL); } + /** + * Get inject wait input + * + * @return the value of 'inject_wait_in_put' or 0 if not set + */ public int getInjectWaitInPut() { Object retVal = this.connectionPropertiesMap.get(SFSessionProperty.INJECT_WAIT_IN_PUT); if (retVal != null) { @@ -600,42 +773,92 @@ public int getInjectWaitInPut() { return 0; } + /** + * Get whether the metadata request should use the session database. + * + * @return true if it should use the session database + */ public boolean getMetadataRequestUseSessionDatabase() { return metadataRequestUseSessionDatabase; } + /** + * Set to true if the metadata request should use the session database. + * + * @param enabled boolean + */ public void setMetadataRequestUseSessionDatabase(boolean enabled) { this.metadataRequestUseSessionDatabase = enabled; } + /** + * Get if metadata request should use the connection ctx + * + * @return true if it should use the connection ctx + */ public boolean getMetadataRequestUseConnectionCtx() { return this.metadataRequestUseConnectionCtx; } + /** + * Set to true if metadata request should use connection ctx + * + * @param enabled boolean + */ public void setMetadataRequestUseConnectionCtx(boolean enabled) { this.metadataRequestUseConnectionCtx = enabled; } + /** + * Get injected delay + * + * @return {@link AtomicInteger} + */ AtomicInteger getInjectedDelay() { return _injectedDelay; } + /** + * Set the injected delay + * + * @param injectedDelay injectedDelay value + */ public void setInjectedDelay(int injectedDelay) { this._injectedDelay.set(injectedDelay); } + /** + * Get if NTZ should be treated as UTC + * + * @return true if NTZ should be treated as UTC + */ public boolean getTreatNTZAsUTC() { return treatNTZAsUTC; } + /** + * Set whether NTZ should be treated as UTC + * + * @param treatNTZAsUTC boolean + */ public void setTreatNTZAsUTC(boolean treatNTZAsUTC) { this.treatNTZAsUTC = treatNTZAsUTC; } + /** + * Get if heartbeat is enabled + * + * @return true if enabled + */ public boolean getEnableHeartbeat() { return enableHeartbeat; } + /** + * Set if heartbeat is enabled + * + * @param enableHeartbeat boolean + */ public void setEnableHeartbeat(boolean enableHeartbeat) { this.enableHeartbeat = enableHeartbeat; } @@ -656,39 +879,88 @@ public void setHeartbeatFrequency(int frequency) { } } - /** Retrieve session heartbeat frequency in seconds */ + /** + * Retrieve session heartbeat frequency in seconds + * + * @return the heartbeat frequency in seconds + */ public int getHeartbeatFrequency() { return this.heartbeatFrequency; } + /** + * autoCommit field specifies whether autocommit is enabled for the session. Autocommit determines + * whether a DML statement, when executed without an active transaction, is automatically + * committed after the statement successfully completes. default: true + * + * @see Transactions/Autocommit + * @return a boolean value of autocommit field + */ public boolean getAutoCommit() { return autoCommit.get(); } + /** + * Sets value of autoCommit field + * + * @see SFBaseSession#getAutoCommit() + * @param autoCommit boolean + */ public void setAutoCommit(boolean autoCommit) { this.autoCommit.set(autoCommit); } + /** + * Get if date should be formatted with timezone + * + * @return true if date should be formatted with timezone + */ public boolean getFormatDateWithTimezone() { return formatDateWithTimezone; } + /** + * Set if date should be formatted with timezone + * + * @param formatDateWithTimezone boolean + */ public void setFormatDateWithTimezone(boolean formatDateWithTimezone) { this.formatDateWithTimezone = formatDateWithTimezone; } + /** + * Get if session timezone should be used. + * + * @return true if using session timezone + */ public boolean getUseSessionTimezone() { return useSessionTimezone; } + /** + * Get if using default date format with timezone. + * + * @return true if using default date format with timezone. + */ public boolean getDefaultFormatDateWithTimezone() { return defaultFormatDateWithTimezone; } + /** + * Set if session timezone should be used. + * + * @param useSessionTimezone boolean + */ public void setUseSessionTimezone(boolean useSessionTimezone) { this.useSessionTimezone = useSessionTimezone; } + /** + * Set if default date format with timezone should be used + * + * @param defaultFormatDateWithTimezone boolean + */ public void setDefaultFormatDateWithTimezone(boolean defaultFormatDateWithTimezone) { this.defaultFormatDateWithTimezone = defaultFormatDateWithTimezone; } @@ -906,6 +1178,7 @@ public void setSessionPropertyByKey(String propertyName, Object propertyValue) { * Fetch the value for a custom session property. * * @param propertyName The key of the session property to fetch. + * @return session property value */ public Object getSessionPropertyByKey(String propertyName) { return this.customSessionProperties.get(propertyName); @@ -914,6 +1187,8 @@ public Object getSessionPropertyByKey(String propertyName) { /** * Function that checks if the active session can be closed when the connection is closed. Called * by SnowflakeConnectionV1. + * + * @return true if the active session is safe to close. */ public abstract boolean isSafeToClose(); @@ -921,7 +1196,7 @@ public Object getSessionPropertyByKey(String propertyName) { * @param queryID query ID of the query whose status is being investigated * @return enum of type QueryStatus indicating the query's status * @deprecated Use {@link #getQueryStatusV2(String)} - * @throws SQLException + * @throws SQLException if error encountered */ @Deprecated public abstract QueryStatus getQueryStatus(String queryID) throws SQLException; @@ -929,13 +1204,15 @@ public Object getSessionPropertyByKey(String propertyName) { /** * @param queryID query ID of the query whose status is being investigated * @return QueryStatusV2 indicating the query's status - * @throws SQLException + * @throws SQLException if error encountered */ public abstract QueryStatusV2 getQueryStatusV2(String queryID) throws SQLException; /** * Validates the connection properties used by this session, and returns a list of missing * properties. + * + * @return List of DriverPropertyInfo */ public abstract List checkProperties(); @@ -948,17 +1225,25 @@ public Object getSessionPropertyByKey(String propertyName) { public abstract void close() throws SFException, SnowflakeSQLException; /** - * Returns the telemetry client, if supported, by this session. If not, should return a - * NoOpTelemetryClient. + * @return Returns the telemetry client, if supported, by this session. If not, should return a + * NoOpTelemetryClient. */ public abstract Telemetry getTelemetryClient(); - /** Makes a heartbeat call to check for session validity. */ + /** + * Makes a heartbeat call to check for session validity. + * + * @param timeout timeout value + * @throws Exception if exception occurs + * @throws SFException if exception occurs + */ public abstract void callHeartBeat(int timeout) throws Exception, SFException; /** * JDBC API. Returns a list of warnings generated since starting this session, or the last time it * was cleared. + * + * @return List of SFException's */ public List getSqlWarnings() { return sqlWarnings; @@ -972,29 +1257,59 @@ public void clearSqlWarnings() { sqlWarnings.clear(); } + /** + * Get the SFConnectionHandler + * + * @return {@link SFConnectionHandler} + */ public SFConnectionHandler getSfConnectionHandler() { return sfConnectionHandler; } + /** + * Get network timeout in milliseconds + * + * @return network timeout in milliseconds + */ public abstract int getNetworkTimeoutInMilli(); + /** + * @return auth timeout in seconds + */ public abstract int getAuthTimeout(); + /** + * @return max http retries + */ public abstract int getMaxHttpRetries(); + /** + * @return {@link SnowflakeConnectString} + */ public abstract SnowflakeConnectString getSnowflakeConnectionString(); + /** + * @return true if this is an async session + */ public abstract boolean isAsyncSession(); + /** + * @return QueryContextDTO containing opaque information shared with the cloud service. + */ public abstract QueryContextDTO getQueryContextDTO(); + /** + * Set query context + * + * @param queryContext the query context string + */ public abstract void setQueryContext(String queryContext); /** - * If true, JDBC will enable returning TIMESTAMP_WITH_TIMEZONE as column type, otherwise it will - * not. This function will always return true for JDBC client, so that the client JDBC will not - * have any behavior change. Stored proc JDBC will override this function to return the value of - * SP_JDBC_ENABLE_TIMESTAMP_WITH_TIMEZONE from server for backward compatibility. + * @return If true, JDBC will enable returning TIMESTAMP_WITH_TIMEZONE as column type, otherwise + * it will not. This function will always return true for JDBC client, so that the client JDBC + * will not have any behavior change. Stored proc JDBC will override this function to return + * the value of SP_JDBC_ENABLE_TIMESTAMP_WITH_TIMEZONE from server for backward compatibility. */ public boolean getEnableReturnTimestampWithTimeZone() { return enableReturnTimestampWithTimeZone; diff --git a/src/main/java/net/snowflake/client/core/SFBaseStatement.java b/src/main/java/net/snowflake/client/core/SFBaseStatement.java index 8a6136434..104d49387 100644 --- a/src/main/java/net/snowflake/client/core/SFBaseStatement.java +++ b/src/main/java/net/snowflake/client/core/SFBaseStatement.java @@ -93,6 +93,7 @@ public abstract SFBaseResultSet execute( * @param sql sql statement. * @param parametersBinding parameters to bind * @param caller the JDBC interface method that called this method, if any + * @param execTimeData ExecTimeTelemetryData * @return whether there is result set or not * @throws SQLException if failed to execute sql * @throws SFException exception raised from Snowflake components @@ -164,8 +165,6 @@ public void executeSetProperty(final String sql) { * A method to check if a sql is file upload statement with consideration for potential comments * in front of put keyword. * - *

- * * @param sql sql statement * @return true if the command is upload statement */ @@ -174,15 +173,25 @@ public static boolean isFileTransfer(String sql) { return statementType == SFStatementType.PUT || statementType == SFStatementType.GET; } - /** If this is a multi-statement, i.e., has child results. */ + /** + * If this is a multi-statement, i.e., has child results. + * + * @return true if has child results + */ public abstract boolean hasChildren(); - /** Returns the SFBaseSession associated with this SFBaseStatement. */ + /** + * Get the SFBaseSession associated with this SFBaseStatement. + * + * @return The SFBaseSession associated with this SFBaseStatement. + */ public abstract SFBaseSession getSFBaseSession(); /** * Retrieves the current result as a ResultSet, if any. This is invoked by SnowflakeStatement and * should return an SFBaseResultSet, which is then wrapped in a SnowflakeResultSet. + * + * @return {@link SFBaseResultSet} */ public abstract SFBaseResultSet getResultSet(); @@ -209,7 +218,9 @@ public enum CallingMethod { public abstract int getConservativePrefetchThreads(); /** + * @param queryID the queryID * @return the child query IDs for the multiple statements query. + * @throws SQLException if an error occurs while getting child query ID's */ public abstract String[] getChildQueryIds(String queryID) throws SQLException; } diff --git a/src/main/java/net/snowflake/client/core/SFException.java b/src/main/java/net/snowflake/client/core/SFException.java index 77c2b1355..a2ea0c551 100644 --- a/src/main/java/net/snowflake/client/core/SFException.java +++ b/src/main/java/net/snowflake/client/core/SFException.java @@ -24,24 +24,47 @@ public class SFException extends Throwable { private int vendorCode; private Object[] params; - /** use {@link SFException#SFException(String, Throwable, ErrorCode, Object...)} */ + /** + * Use {@link SFException#SFException(String, Throwable, ErrorCode, Object...)} + * + * @param errorCode the error code + * @param params additional params + */ @Deprecated public SFException(ErrorCode errorCode, Object... params) { this(null, null, errorCode, params); } - /** use {@link SFException#SFException(String, Throwable, ErrorCode, Object...)} */ + /** + * use {@link SFException#SFException(String, Throwable, ErrorCode, Object...)} + * + * @param queryID the query id + * @param errorCode the error code + * @param params additional params + */ @Deprecated public SFException(String queryID, ErrorCode errorCode, Object... params) { this(queryID, null, errorCode, params); } - /** use {@link SFException#SFException(String, Throwable, ErrorCode, Object...)} */ + /** + * use {@link SFException#SFException(String, Throwable, ErrorCode, Object...)} + * + * @param cause throwable + * @param errorCode error code + * @param params additional params + */ @Deprecated public SFException(Throwable cause, ErrorCode errorCode, Object... params) { this(null, cause, errorCode, params); } + /** + * @param queryId query ID + * @param cause throwable + * @param errorCode error code + * @param params additional params + */ public SFException(String queryId, Throwable cause, ErrorCode errorCode, Object... params) { super( errorResourceBundleManager.getLocalizedMessage( @@ -55,22 +78,47 @@ public SFException(String queryId, Throwable cause, ErrorCode errorCode, Object. this.params = params; } + /** + * Get the error cause + * + * @return Throwable + */ public Throwable getCause() { return cause; } + /** + * Get the query ID + * + * @return query ID string + */ public String getQueryId() { return queryId; } + /** + * Get the SQL state + * + * @return SQL state string + */ public String getSqlState() { return sqlState; } + /** + * Get the vendor code + * + * @return vendor code + */ public int getVendorCode() { return vendorCode; } + /** + * Get additional parameters + * + * @return parameter array + */ public Object[] getParams() { return params; } diff --git a/src/main/java/net/snowflake/client/core/SFJsonResultSet.java b/src/main/java/net/snowflake/client/core/SFJsonResultSet.java index 1011870df..c32a16424 100644 --- a/src/main/java/net/snowflake/client/core/SFJsonResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFJsonResultSet.java @@ -108,7 +108,7 @@ public Object getObject(int columnIndex) throws SFException { * * @param columnIndex the column index * @return an object of type long or BigDecimal depending on number size - * @throws SFException + * @throws SFException if an error occurs */ private Object getBigInt(int columnIndex, Object obj) throws SFException { return converters.getNumberConverter().getBigInt(obj, columnIndex); diff --git a/src/main/java/net/snowflake/client/core/SFResultSet.java b/src/main/java/net/snowflake/client/core/SFResultSet.java index fee90a3ee..7c66e1f5a 100644 --- a/src/main/java/net/snowflake/client/core/SFResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFResultSet.java @@ -30,8 +30,6 @@ /** * Snowflake ResultSet implementation * - *

- * * @author jhuang */ public class SFResultSet extends SFJsonResultSet { @@ -129,7 +127,7 @@ public SFResultSet( * @param resultSetSerializable data returned in query response * @param telemetryClient telemetryClient * @param sortResult should sorting take place - * @throws SQLException + * @throws SQLException if exception is encountered */ public SFResultSet( SnowflakeResultSetSerializableV1 resultSetSerializable, @@ -147,7 +145,7 @@ public SFResultSet( * @param session snowflake session * @param telemetryClient telemetryClient * @param sortResult should sorting take place - * @throws SQLException + * @throws SQLException if an exception is encountered. */ public SFResultSet( SnowflakeResultSetSerializableV1 resultSetSerializable, diff --git a/src/main/java/net/snowflake/client/core/SFSession.java b/src/main/java/net/snowflake/client/core/SFSession.java index 8e2e834a0..c3708ea7e 100644 --- a/src/main/java/net/snowflake/client/core/SFSession.java +++ b/src/main/java/net/snowflake/client/core/SFSession.java @@ -290,7 +290,7 @@ else if (ex instanceof SFException) { /** * @param queryID query ID of the query whose status is being investigated * @return enum of type QueryStatus indicating the query's status - * @throws SQLException + * @throws SQLException if an error is encountered * @deprecated the returned enum is error-prone, use {@link #getQueryStatusV2} instead */ @Deprecated @@ -337,7 +337,7 @@ else if (isAnError(result)) { /** * @param queryID query ID of the query whose status is being investigated * @return a QueryStatusV2 instance indicating the query's status - * @throws SQLException + * @throws SQLException if an error is encountered */ public QueryStatusV2 getQueryStatusV2(String queryID) throws SQLException { JsonNode queryNode = getQueryMetadata(queryID); diff --git a/src/main/java/net/snowflake/client/core/SFSqlInput.java b/src/main/java/net/snowflake/client/core/SFSqlInput.java index 2b3d6ba95..6ca9988d9 100644 --- a/src/main/java/net/snowflake/client/core/SFSqlInput.java +++ b/src/main/java/net/snowflake/client/core/SFSqlInput.java @@ -37,6 +37,7 @@ static SFSqlInput unwrap(SQLInput sqlInput) { * * @param the type of the class modeled by this Class object * @param type Class representing the Java data type to convert the attribute to. + * @param tz timezone to consider. * @return the attribute at the head of the stream as an {@code Object} in the Java programming * language;{@code null} if the attribute is SQL {@code NULL} * @exception SQLException if a database access error occurs diff --git a/src/main/java/net/snowflake/client/core/SFStatement.java b/src/main/java/net/snowflake/client/core/SFStatement.java index f3a0f8a09..173ecf21f 100644 --- a/src/main/java/net/snowflake/client/core/SFStatement.java +++ b/src/main/java/net/snowflake/client/core/SFStatement.java @@ -318,6 +318,8 @@ public Void call() throws SQLException { * @param bindValues map of binding values * @param describeOnly whether only show the result set metadata * @param internal run internal query not showing up in history + * @param asyncExec is async execute + * @param execTimeData ExecTimeTelemetryData * @return raw json response * @throws SFException if query is canceled * @throws SnowflakeSQLException if query is already running @@ -752,8 +754,10 @@ private void cancelHelper(String sql, String mediaType, CancellationReason cance * Execute sql * * @param sql sql statement. + * @param asyncExec is async exec * @param parametersBinding parameters to bind * @param caller the JDBC interface method that called this method, if any + * @param execTimeData ExecTimeTelemetryData * @return whether there is result set or not * @throws SQLException if failed to execute sql * @throws SFException exception raised from Snowflake components diff --git a/src/main/java/net/snowflake/client/core/SessionUtil.java b/src/main/java/net/snowflake/client/core/SessionUtil.java index a12f20ce3..de0eb3a87 100644 --- a/src/main/java/net/snowflake/client/core/SessionUtil.java +++ b/src/main/java/net/snowflake/client/core/SessionUtil.java @@ -953,11 +953,22 @@ private static String nullStringAsEmptyString(String value) { return value; } - /** Delete the id token cache */ + /** + * Delete the id token cache + * + * @param host The host string + * @param user The user + */ public static void deleteIdTokenCache(String host, String user) { CredentialManager.getInstance().deleteIdTokenCache(host, user); } + /** + * Delete the mfa token cache + * + * @param host The host string + * @param user The user + */ public static void deleteMfaTokenCache(String host, String user) { CredentialManager.getInstance().deleteMfaTokenCache(host, user); } @@ -1710,6 +1721,7 @@ enum TokenRequestType { * private link, do nothing. * * @param serverUrl The Snowflake URL includes protocol such as "https://" + * @throws IOException If exception encountered */ public static void resetOCSPUrlIfNecessary(String serverUrl) throws IOException { if (PrivateLinkDetector.isPrivateLink(serverUrl)) { diff --git a/src/main/java/net/snowflake/client/core/SnowflakeMutableProxyRoutePlanner.java b/src/main/java/net/snowflake/client/core/SnowflakeMutableProxyRoutePlanner.java index 3b371536d..31e6af391 100644 --- a/src/main/java/net/snowflake/client/core/SnowflakeMutableProxyRoutePlanner.java +++ b/src/main/java/net/snowflake/client/core/SnowflakeMutableProxyRoutePlanner.java @@ -29,6 +29,10 @@ public class SnowflakeMutableProxyRoutePlanner implements HttpRoutePlanner, Seri /** * @deprecated Use {@link #SnowflakeMutableProxyRoutePlanner(String, int, HttpProtocol, String)} * instead + * @param host host + * @param proxyPort proxy port + * @param proxyProtocol proxy protocol + * @param nonProxyHosts non-proxy hosts */ @Deprecated public SnowflakeMutableProxyRoutePlanner( @@ -36,6 +40,12 @@ public SnowflakeMutableProxyRoutePlanner( this(host, proxyPort, toSnowflakeProtocol(proxyProtocol), nonProxyHosts); } + /** + * @param host host + * @param proxyPort proxy port + * @param proxyProtocol proxy protocol + * @param nonProxyHosts non-proxy hosts + */ public SnowflakeMutableProxyRoutePlanner( String host, int proxyPort, HttpProtocol proxyProtocol, String nonProxyHosts) { proxyRoutePlanner = @@ -46,12 +56,20 @@ public SnowflakeMutableProxyRoutePlanner( this.protocol = proxyProtocol; } + /** + * Set non-proxy hosts + * + * @param nonProxyHosts non-proxy hosts + */ public void setNonProxyHosts(String nonProxyHosts) { this.nonProxyHosts = nonProxyHosts; proxyRoutePlanner = new SdkProxyRoutePlanner(host, proxyPort, toAwsProtocol(protocol), nonProxyHosts); } + /** + * @return non-proxy hosts string + */ public String getNonProxyHosts() { return nonProxyHosts; } diff --git a/src/main/java/net/snowflake/client/core/StmtUtil.java b/src/main/java/net/snowflake/client/core/StmtUtil.java index 3d8055d1f..18b7ae7f7 100644 --- a/src/main/java/net/snowflake/client/core/StmtUtil.java +++ b/src/main/java/net/snowflake/client/core/StmtUtil.java @@ -270,6 +270,7 @@ public JsonNode getResult() { * submission, but continue the ping pong process. * * @param stmtInput input statement + * @param execTimeData ExecTimeTelemetryData * @return StmtOutput output statement * @throws SFException exception raised from Snowflake components * @throws SnowflakeSQLException exception raised from Snowflake components @@ -584,8 +585,6 @@ protected static String getQueryResult( /** * Issue get-result call to get query result given an in-progress response. * - *

- * * @param getResultPath path to results * @param stmtInput object with context information * @return results in string form @@ -645,8 +644,6 @@ protected static String getQueryResult(String getResultPath, StmtInput stmtInput /** * Issue get-result call to get query result given an in progress response. * - *

- * * @param queryId id of query to get results for * @param session the current session * @return results in JSON diff --git a/src/main/java/net/snowflake/client/core/arrow/AbstractArrowVectorConverter.java b/src/main/java/net/snowflake/client/core/arrow/AbstractArrowVectorConverter.java index c7054442c..855f128b2 100644 --- a/src/main/java/net/snowflake/client/core/arrow/AbstractArrowVectorConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/AbstractArrowVectorConverter.java @@ -43,9 +43,18 @@ abstract class AbstractArrowVectorConverter implements ArrowVectorConverter { /** Field names of the struct vectors used by timestamp */ public static final String FIELD_NAME_EPOCH = "epoch"; // seconds since epoch + /** Timezone index */ public static final String FIELD_NAME_TIME_ZONE_INDEX = "timezone"; // time zone index + + /** Fraction in nanoseconds */ public static final String FIELD_NAME_FRACTION = "fraction"; // fraction in nanoseconds + /** + * @param logicalTypeStr snowflake logical type of the target arrow vector. + * @param valueVector value vector + * @param vectorIndex value index + * @param context DataConversionContext + */ AbstractArrowVectorConverter( String logicalTypeStr, ValueVector valueVector, @@ -153,6 +162,11 @@ public BigDecimal toBigDecimal(int index) throws SFException { ErrorCode.INVALID_VALUE_CONVERT, logicalTypeStr, SnowflakeUtil.BIG_DECIMAL_STR, ""); } + /** + * True if should treat decimal as int type. + * + * @return true or false if decimal should be treated as int type. + */ boolean shouldTreatDecimalAsInt() { return shouldTreatDecimalAsInt; } diff --git a/src/main/java/net/snowflake/client/core/arrow/ArrayConverter.java b/src/main/java/net/snowflake/client/core/arrow/ArrayConverter.java index 48b8fa083..ad9926eac 100644 --- a/src/main/java/net/snowflake/client/core/arrow/ArrayConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/ArrayConverter.java @@ -8,10 +8,16 @@ import org.apache.arrow.vector.FieldVector; import org.apache.arrow.vector.complex.ListVector; +/** Array type converter. */ public class ArrayConverter extends AbstractArrowVectorConverter { private final ListVector vector; + /** + * @param valueVector ListVector + * @param vectorIndex vector index + * @param context DataConversionContext + */ public ArrayConverter(ListVector valueVector, int vectorIndex, DataConversionContext context) { super(SnowflakeType.ARRAY.name(), valueVector, vectorIndex, context); this.vector = valueVector; diff --git a/src/main/java/net/snowflake/client/core/arrow/ArrowResultChunkIndexSorter.java b/src/main/java/net/snowflake/client/core/arrow/ArrowResultChunkIndexSorter.java index 0478b2996..5966447e8 100644 --- a/src/main/java/net/snowflake/client/core/arrow/ArrowResultChunkIndexSorter.java +++ b/src/main/java/net/snowflake/client/core/arrow/ArrowResultChunkIndexSorter.java @@ -41,7 +41,7 @@ private void initIndices() { * This method is only used when sf-property sort is on * * @return sorted indices - * @throws SFException + * @throws SFException when exception encountered */ public IntVector sort() throws SFException { quickSort(0, resultChunk.get(0).getValueCount() - 1); diff --git a/src/main/java/net/snowflake/client/core/arrow/ArrowResultUtil.java b/src/main/java/net/snowflake/client/core/arrow/ArrowResultUtil.java index 2ad5c3ef2..03d5c03e8 100644 --- a/src/main/java/net/snowflake/client/core/arrow/ArrowResultUtil.java +++ b/src/main/java/net/snowflake/client/core/arrow/ArrowResultUtil.java @@ -46,7 +46,7 @@ public static String getStringFormat(int scale) { /** * new method to get Date from integer * - * @param day + * @param day The day to convert. * @return Date */ public static Date getDate(int day) { @@ -57,11 +57,11 @@ public static Date getDate(int day) { /** * Method to get Date from integer using timezone offsets * - * @param day - * @param oldTz - * @param newTz - * @return - * @throws SFException + * @param day The day to convert. + * @param oldTz The old timezone. + * @param newTz The new timezone. + * @return Date + * @throws SFException if date value is invalid */ public static Date getDate(int day, TimeZone oldTz, TimeZone newTz) throws SFException { try { @@ -90,10 +90,10 @@ public static Date getDate(int day, TimeZone oldTz, TimeZone newTz) throws SFExc /** * simplified moveToTimeZone method * - * @param milliSecsSinceEpoch - * @param oldTZ - * @param newTZ - * @return offset + * @param milliSecsSinceEpoch milliseconds since Epoch + * @param oldTZ old timezone + * @param newTZ new timezone + * @return offset offset value */ private static long moveToTimeZoneOffset( long milliSecsSinceEpoch, TimeZone oldTZ, TimeZone newTZ) { @@ -128,9 +128,9 @@ private static long moveToTimeZoneOffset( /** * move the input timestamp form oldTZ to newTZ * - * @param ts - * @param oldTZ - * @param newTZ + * @param ts Timestamp + * @param oldTZ Old timezone + * @param newTZ New timezone * @return timestamp in newTZ */ public static Timestamp moveToTimeZone(Timestamp ts, TimeZone oldTZ, TimeZone newTZ) { @@ -149,7 +149,7 @@ public static Timestamp moveToTimeZone(Timestamp ts, TimeZone oldTZ, TimeZone ne * * @param epoch the value since epoch time * @param scale the scale of the value - * @return + * @return Timestamp */ public static Timestamp toJavaTimestamp(long epoch, int scale) { return toJavaTimestamp(epoch, scale, TimeZone.getDefault(), false); @@ -160,7 +160,9 @@ public static Timestamp toJavaTimestamp(long epoch, int scale) { * * @param epoch the value since epoch time * @param scale the scale of the value - * @return + * @param sessionTimezone the session timezone + * @param useSessionTimezone should the session timezone be used + * @return Timestamp */ @SnowflakeJdbcInternalApi public static Timestamp toJavaTimestamp( @@ -178,8 +180,8 @@ public static Timestamp toJavaTimestamp( /** * check whether the input seconds out of the scope of Java timestamp * - * @param seconds - * @return + * @param seconds long value to check + * @return true if value is out of the scope of Java timestamp. */ public static boolean isTimestampOverflow(long seconds) { return seconds < Long.MIN_VALUE / powerOfTen(3) || seconds > Long.MAX_VALUE / powerOfTen(3); @@ -191,10 +193,10 @@ public static boolean isTimestampOverflow(long seconds) { * represents as epoch = -1233 and fraction = 766,000,000 For example, -0.13 represents as epoch = * -1 and fraction = 870,000,000 * - * @param seconds - * @param fraction - * @param timezone - The timezone being used for the toString() formatting - * @param timezone - + * @param seconds seconds value + * @param fraction fraction + * @param timezone The timezone being used for the toString() formatting + * @param useSessionTz boolean useSessionTz * @return java timestamp object */ public static Timestamp createTimestamp( diff --git a/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverter.java b/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverter.java index f61e9954d..1a1cff542 100644 --- a/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverter.java @@ -16,7 +16,7 @@ public interface ArrowVectorConverter { /** * Set to true when time value should be displayed in wallclock time (no timezone offset) * - * @param useSessionTimezone + * @param useSessionTimezone boolean value indicating if there is a timezone offset. */ void setUseSessionTimezone(boolean useSessionTimezone); @@ -160,6 +160,8 @@ public interface ArrowVectorConverter { Object toObject(int index) throws SFException; /** + * Set to true if NTZ timestamp should be set to UTC + * * @param isUTC true or false value of whether NTZ timestamp should be set to UTC */ void setTreatNTZAsUTC(boolean isUTC); diff --git a/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverterUtil.java b/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverterUtil.java index 0231ebd51..68ccd2a14 100644 --- a/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverterUtil.java +++ b/src/main/java/net/snowflake/client/core/arrow/ArrowVectorConverterUtil.java @@ -36,8 +36,6 @@ public static SnowflakeType getSnowflakeTypeFromFieldMetadata(Field field) { * converter. Note, converter is built on top of arrow vector, so that arrow data can be converted * back to java data * - *

- * *

Arrow converter mappings for Snowflake fixed-point numbers * ----------------------------------------------------------------------------------------- Max * position and scale Converter @@ -54,6 +52,7 @@ public static SnowflakeType getSnowflakeTypeFromFieldMetadata(Field field) { * @param session SFBaseSession for purposes of logging * @param idx the index of the vector in its batch * @return A converter on top og the vector + * @throws SnowflakeSQLException if error encountered */ public static ArrowVectorConverter initConverter( ValueVector vector, DataConversionContext context, SFBaseSession session, int idx) diff --git a/src/main/java/net/snowflake/client/core/arrow/BigIntToFixedConverter.java b/src/main/java/net/snowflake/client/core/arrow/BigIntToFixedConverter.java index 71bd123a0..13a026e4f 100644 --- a/src/main/java/net/snowflake/client/core/arrow/BigIntToFixedConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/BigIntToFixedConverter.java @@ -23,6 +23,11 @@ public class BigIntToFixedConverter extends AbstractArrowVectorConverter { protected ByteBuffer byteBuf = ByteBuffer.allocate(BigIntVector.TYPE_WIDTH); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public BigIntToFixedConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super( diff --git a/src/main/java/net/snowflake/client/core/arrow/BigIntToTimeConverter.java b/src/main/java/net/snowflake/client/core/arrow/BigIntToTimeConverter.java index 74d01f98a..87b3d43d1 100644 --- a/src/main/java/net/snowflake/client/core/arrow/BigIntToTimeConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/BigIntToTimeConverter.java @@ -18,10 +18,16 @@ import org.apache.arrow.vector.BigIntVector; import org.apache.arrow.vector.ValueVector; +/** BigInt to Time type converter. */ public class BigIntToTimeConverter extends AbstractArrowVectorConverter { private BigIntVector bigIntVector; protected ByteBuffer byteBuf = ByteBuffer.allocate(BigIntVector.TYPE_WIDTH); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public BigIntToTimeConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.TIME.name(), fieldVector, columnIndex, context); @@ -49,6 +55,15 @@ public Time toTime(int index) throws SFException { } } + /** + * Return the long value as a Time object. + * + * @param value long value to represent as Time + * @param scale the scale + * @param useSessionTimezone boolean indicating use of session timezone + * @return Time object representing the value + * @throws SFException invalid data conversion + */ public static Time getTime(long value, int scale, boolean useSessionTimezone) throws SFException { SFTime sfTime = SFTime.fromFractionalSeconds(value, scale); Time ts = diff --git a/src/main/java/net/snowflake/client/core/arrow/BigIntToTimestampLTZConverter.java b/src/main/java/net/snowflake/client/core/arrow/BigIntToTimestampLTZConverter.java index e2bba45ab..774a0cd74 100644 --- a/src/main/java/net/snowflake/client/core/arrow/BigIntToTimestampLTZConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/BigIntToTimestampLTZConverter.java @@ -23,6 +23,11 @@ public class BigIntToTimestampLTZConverter extends AbstractArrowVectorConverter private BigIntVector bigIntVector; private ByteBuffer byteBuf = ByteBuffer.allocate(BigIntVector.TYPE_WIDTH); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public BigIntToTimestampLTZConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.TIMESTAMP_LTZ.name(), fieldVector, columnIndex, context); @@ -97,7 +102,7 @@ public boolean toBoolean(int index) throws SFException { * @param val epoch * @param scale scale * @return Timestamp value without timezone take into account - * @throws SFException + * @throws SFException if exception encountered */ @Deprecated public static Timestamp getTimestamp(long val, int scale) throws SFException { diff --git a/src/main/java/net/snowflake/client/core/arrow/BigIntToTimestampNTZConverter.java b/src/main/java/net/snowflake/client/core/arrow/BigIntToTimestampNTZConverter.java index cec64d59e..82d107209 100644 --- a/src/main/java/net/snowflake/client/core/arrow/BigIntToTimestampNTZConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/BigIntToTimestampNTZConverter.java @@ -24,6 +24,11 @@ public class BigIntToTimestampNTZConverter extends AbstractArrowVectorConverter private static final TimeZone NTZ = TimeZone.getTimeZone("UTC"); private ByteBuffer byteBuf = ByteBuffer.allocate(BigIntVector.TYPE_WIDTH); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public BigIntToTimestampNTZConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.TIMESTAMP_NTZ.name(), fieldVector, columnIndex, context); diff --git a/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java b/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java index 2f5a8cf83..cddc7b3b4 100644 --- a/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/BitToBooleanConverter.java @@ -14,6 +14,11 @@ public class BitToBooleanConverter extends AbstractArrowVectorConverter { private BitVector bitVector; + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public BitToBooleanConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.BOOLEAN.name(), fieldVector, columnIndex, context); diff --git a/src/main/java/net/snowflake/client/core/arrow/DateConverter.java b/src/main/java/net/snowflake/client/core/arrow/DateConverter.java index a6f50e388..7d18417e2 100644 --- a/src/main/java/net/snowflake/client/core/arrow/DateConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/DateConverter.java @@ -31,6 +31,12 @@ public DateConverter(ValueVector fieldVector, int columnIndex, DataConversionCon this.useDateFormat = false; } + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + * @param useDateFormat boolean indicates whether to use session timezone + */ public DateConverter( ValueVector fieldVector, int columnIndex, diff --git a/src/main/java/net/snowflake/client/core/arrow/DecimalToScaledFixedConverter.java b/src/main/java/net/snowflake/client/core/arrow/DecimalToScaledFixedConverter.java index 259913d95..b6d9b7a0b 100644 --- a/src/main/java/net/snowflake/client/core/arrow/DecimalToScaledFixedConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/DecimalToScaledFixedConverter.java @@ -17,6 +17,11 @@ public class DecimalToScaledFixedConverter extends AbstractArrowVectorConverter { protected DecimalVector decimalVector; + /** + * @param fieldVector ValueVector + * @param vectorIndex vector index + * @param context DataConversionContext + */ public DecimalToScaledFixedConverter( ValueVector fieldVector, int vectorIndex, DataConversionContext context) { super( diff --git a/src/main/java/net/snowflake/client/core/arrow/DoubleToRealConverter.java b/src/main/java/net/snowflake/client/core/arrow/DoubleToRealConverter.java index d2f925867..731407861 100644 --- a/src/main/java/net/snowflake/client/core/arrow/DoubleToRealConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/DoubleToRealConverter.java @@ -13,10 +13,16 @@ import org.apache.arrow.vector.Float8Vector; import org.apache.arrow.vector.ValueVector; +/** Convert from Arrow Float8Vector to Real. */ public class DoubleToRealConverter extends AbstractArrowVectorConverter { private Float8Vector float8Vector; private ByteBuffer byteBuf = ByteBuffer.allocate(Float8Vector.TYPE_WIDTH); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public DoubleToRealConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.REAL.name(), fieldVector, columnIndex, context); diff --git a/src/main/java/net/snowflake/client/core/arrow/IntToFixedConverter.java b/src/main/java/net/snowflake/client/core/arrow/IntToFixedConverter.java index 8055081ef..8cca3c930 100644 --- a/src/main/java/net/snowflake/client/core/arrow/IntToFixedConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/IntToFixedConverter.java @@ -18,6 +18,11 @@ public class IntToFixedConverter extends AbstractArrowVectorConverter { protected int sfScale; protected ByteBuffer byteBuf = ByteBuffer.allocate(IntVector.TYPE_WIDTH); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public IntToFixedConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super( diff --git a/src/main/java/net/snowflake/client/core/arrow/IntToTimeConverter.java b/src/main/java/net/snowflake/client/core/arrow/IntToTimeConverter.java index d704e31bd..27ca0b4ad 100644 --- a/src/main/java/net/snowflake/client/core/arrow/IntToTimeConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/IntToTimeConverter.java @@ -18,10 +18,16 @@ import org.apache.arrow.vector.IntVector; import org.apache.arrow.vector.ValueVector; +/** Convert from Arrow IntVector to Time. */ public class IntToTimeConverter extends AbstractArrowVectorConverter { private IntVector intVector; private ByteBuffer byteBuf = ByteBuffer.allocate(IntVector.TYPE_WIDTH); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public IntToTimeConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.TIME.name(), fieldVector, columnIndex, context); diff --git a/src/main/java/net/snowflake/client/core/arrow/MapConverter.java b/src/main/java/net/snowflake/client/core/arrow/MapConverter.java index 8ef1cdccf..4099cd5fb 100644 --- a/src/main/java/net/snowflake/client/core/arrow/MapConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/MapConverter.java @@ -11,10 +11,16 @@ import org.apache.arrow.vector.complex.MapVector; import org.apache.arrow.vector.util.JsonStringHashMap; +/** Arrow MapVector converter. */ public class MapConverter extends AbstractArrowVectorConverter { private final MapVector vector; + /** + * @param valueVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public MapConverter(MapVector valueVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.MAP.name(), valueVector, columnIndex, context); this.vector = valueVector; diff --git a/src/main/java/net/snowflake/client/core/arrow/SmallIntToFixedConverter.java b/src/main/java/net/snowflake/client/core/arrow/SmallIntToFixedConverter.java index bfa398d88..13aa87db5 100644 --- a/src/main/java/net/snowflake/client/core/arrow/SmallIntToFixedConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/SmallIntToFixedConverter.java @@ -18,6 +18,11 @@ public class SmallIntToFixedConverter extends AbstractArrowVectorConverter { protected SmallIntVector smallIntVector; ByteBuffer byteBuf = ByteBuffer.allocate(SmallIntVector.TYPE_WIDTH); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public SmallIntToFixedConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super( diff --git a/src/main/java/net/snowflake/client/core/arrow/ThreeFieldStructToTimestampTZConverter.java b/src/main/java/net/snowflake/client/core/arrow/ThreeFieldStructToTimestampTZConverter.java index 88d3e53ba..929045dd1 100644 --- a/src/main/java/net/snowflake/client/core/arrow/ThreeFieldStructToTimestampTZConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/ThreeFieldStructToTimestampTZConverter.java @@ -29,6 +29,11 @@ public class ThreeFieldStructToTimestampTZConverter extends AbstractArrowVectorC private IntVector timeZoneIndices; private TimeZone timeZone = TimeZone.getTimeZone("UTC"); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public ThreeFieldStructToTimestampTZConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.TIMESTAMP_LTZ.name(), fieldVector, columnIndex, context); diff --git a/src/main/java/net/snowflake/client/core/arrow/TinyIntToFixedConverter.java b/src/main/java/net/snowflake/client/core/arrow/TinyIntToFixedConverter.java index 26c90c228..ace873f7f 100644 --- a/src/main/java/net/snowflake/client/core/arrow/TinyIntToFixedConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/TinyIntToFixedConverter.java @@ -17,6 +17,11 @@ public class TinyIntToFixedConverter extends AbstractArrowVectorConverter { protected TinyIntVector tinyIntVector; protected int sfScale = 0; + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public TinyIntToFixedConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super( diff --git a/src/main/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampLTZConverter.java b/src/main/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampLTZConverter.java index 86eeb93b8..6e3904751 100644 --- a/src/main/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampLTZConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampLTZConverter.java @@ -26,6 +26,11 @@ public class TwoFieldStructToTimestampLTZConverter extends AbstractArrowVectorCo private BigIntVector epochs; private IntVector fractions; + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public TwoFieldStructToTimestampLTZConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.TIMESTAMP_LTZ.name(), fieldVector, columnIndex, context); diff --git a/src/main/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampNTZConverter.java b/src/main/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampNTZConverter.java index f4d0d9417..30467169e 100644 --- a/src/main/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampNTZConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampNTZConverter.java @@ -27,6 +27,11 @@ public class TwoFieldStructToTimestampNTZConverter extends AbstractArrowVectorCo private static final TimeZone NTZ = TimeZone.getTimeZone("UTC"); + /** + * @param fieldVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public TwoFieldStructToTimestampNTZConverter( ValueVector fieldVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.TIMESTAMP_NTZ.name(), fieldVector, columnIndex, context); diff --git a/src/main/java/net/snowflake/client/core/arrow/VarBinaryToBinaryConverter.java b/src/main/java/net/snowflake/client/core/arrow/VarBinaryToBinaryConverter.java index f45e561f4..2c4774fb0 100644 --- a/src/main/java/net/snowflake/client/core/arrow/VarBinaryToBinaryConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/VarBinaryToBinaryConverter.java @@ -11,9 +11,15 @@ import org.apache.arrow.vector.ValueVector; import org.apache.arrow.vector.VarBinaryVector; +/** Converter from Arrow VarBinaryVector to Binary. */ public class VarBinaryToBinaryConverter extends AbstractArrowVectorConverter { private VarBinaryVector varBinaryVector; + /** + * @param valueVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public VarBinaryToBinaryConverter( ValueVector valueVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.BINARY.name(), valueVector, columnIndex, context); diff --git a/src/main/java/net/snowflake/client/core/arrow/VarCharConverter.java b/src/main/java/net/snowflake/client/core/arrow/VarCharConverter.java index 8a6ce64e5..b53595d42 100644 --- a/src/main/java/net/snowflake/client/core/arrow/VarCharConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/VarCharConverter.java @@ -22,6 +22,11 @@ public class VarCharConverter extends AbstractArrowVectorConverter { private VarCharVector varCharVector; + /** + * @param valueVector ValueVector + * @param columnIndex column index + * @param context DataConversionContext + */ public VarCharConverter(ValueVector valueVector, int columnIndex, DataConversionContext context) { super(SnowflakeType.TEXT.name(), valueVector, columnIndex, context); this.varCharVector = (VarCharVector) valueVector; diff --git a/src/main/java/net/snowflake/client/core/arrow/VectorTypeConverter.java b/src/main/java/net/snowflake/client/core/arrow/VectorTypeConverter.java index ae7a492a0..8d1ae2942 100644 --- a/src/main/java/net/snowflake/client/core/arrow/VectorTypeConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/VectorTypeConverter.java @@ -6,10 +6,16 @@ import net.snowflake.client.jdbc.SnowflakeType; import org.apache.arrow.vector.complex.FixedSizeListVector; +/** Arrow FixedSizeListVector converter. */ public class VectorTypeConverter extends AbstractArrowVectorConverter { private final FixedSizeListVector vector; + /** + * @param valueVector ValueVector + * @param vectorIndex vector index + * @param context DataConversionContext + */ public VectorTypeConverter( FixedSizeListVector valueVector, int vectorIndex, DataConversionContext context) { super(SnowflakeType.ARRAY.name(), valueVector, vectorIndex, context); diff --git a/src/main/java/net/snowflake/client/core/bind/BindUploader.java b/src/main/java/net/snowflake/client/core/bind/BindUploader.java index 6b901da44..ed1f11249 100644 --- a/src/main/java/net/snowflake/client/core/bind/BindUploader.java +++ b/src/main/java/net/snowflake/client/core/bind/BindUploader.java @@ -187,7 +187,13 @@ public static synchronized BindUploader newInstance(SFBaseSession session, Strin return new BindUploader(session, stageDir); } - /** Wrapper around upload() with default compression to true. */ + /** + * Wrapper around upload() with default compression to true. + * + * @param bindValues the bind map to upload + * @throws BindException if there is an error when uploading bind values + * @throws SQLException if any error occurs + */ public void upload(Map bindValues) throws BindException, SQLException { upload(bindValues, true); @@ -199,8 +205,8 @@ public void upload(Map bindValues) * * @param bindValues the bind map to upload * @param compressData whether or not to compress data - * @throws BindException - * @throws SQLException + * @throws BindException if there is an error when uploading bind values + * @throws SQLException if any error occurs */ public void upload(Map bindValues, boolean compressData) throws BindException, SQLException { @@ -254,6 +260,7 @@ public void upload(Map bindValues, boolean compress * @param destFileName destination file name to use * @param compressData whether compression is requested fore uploading data * @throws SQLException raises if any error occurs + * @throws BindException if there is an error when uploading bind values */ private void uploadStreamInternal( InputStream inputStream, String destFileName, boolean compressData) diff --git a/src/main/java/net/snowflake/client/jdbc/ArrowResultChunk.java b/src/main/java/net/snowflake/client/jdbc/ArrowResultChunk.java index f69469542..cf641dc10 100644 --- a/src/main/java/net/snowflake/client/jdbc/ArrowResultChunk.java +++ b/src/main/java/net/snowflake/client/jdbc/ArrowResultChunk.java @@ -138,12 +138,16 @@ public void freeData() { } /** + * @param dataConversionContext DataConversionContext * @return an iterator to iterate over current chunk */ public ArrowChunkIterator getIterator(DataConversionContext dataConversionContext) { return new ArrowChunkIterator(dataConversionContext); } + /** + * @return an empty iterator to iterate over current chunk + */ public static ArrowChunkIterator getEmptyChunkIterator() { return new EmptyArrowResultChunk().new ArrowChunkIterator(null); } @@ -200,7 +204,12 @@ private List initConverters(List vectors) return converters; } - /** advance to next row */ + /** + * Advance to next row. + * + * @return true if there is a next row + * @throws SnowflakeSQLException if an error is encountered. + */ public boolean next() throws SnowflakeSQLException { currentRowInRecordBatch++; if (currentRowInRecordBatch < rowCountInCurrentRecordBatch) { @@ -270,6 +279,8 @@ public int getCurrentRowInRecordBatch() { /** * merge arrow result chunk with more than one batches into one record batch (Only used for the * first chunk when client side sorting is required) + * + * @throws SnowflakeSQLException if failed to merge first result chunk */ public void mergeBatchesIntoOne() throws SnowflakeSQLException { try { diff --git a/src/main/java/net/snowflake/client/jdbc/DefaultSFConnectionHandler.java b/src/main/java/net/snowflake/client/jdbc/DefaultSFConnectionHandler.java index 6bb62c82f..67151bea8 100644 --- a/src/main/java/net/snowflake/client/jdbc/DefaultSFConnectionHandler.java +++ b/src/main/java/net/snowflake/client/jdbc/DefaultSFConnectionHandler.java @@ -81,6 +81,7 @@ public DefaultSFConnectionHandler(SnowflakeConnectString conStr, boolean skipOpe * schemaName from the URL if it is specified there. * * @param conStr Connection string object + * @return a map containing accountName, databaseName and schemaName if specified */ public static Map mergeProperties(SnowflakeConnectString conStr) { conStr.getParameters().remove("SSL"); diff --git a/src/main/java/net/snowflake/client/jdbc/QueryStatusV2.java b/src/main/java/net/snowflake/client/jdbc/QueryStatusV2.java index 743447ef0..a40976461 100644 --- a/src/main/java/net/snowflake/client/jdbc/QueryStatusV2.java +++ b/src/main/java/net/snowflake/client/jdbc/QueryStatusV2.java @@ -127,7 +127,11 @@ public String getWarehouseServerType() { return warehouseServerType; } - /** To preserve compatibility with {@link QueryStatus} */ + /** + * To preserve compatibility with {@link QueryStatus} + * + * @return name + */ public String getDescription() { return name; } diff --git a/src/main/java/net/snowflake/client/jdbc/RestRequest.java b/src/main/java/net/snowflake/client/jdbc/RestRequest.java index c753c87de..35e61efd9 100644 --- a/src/main/java/net/snowflake/client/jdbc/RestRequest.java +++ b/src/main/java/net/snowflake/client/jdbc/RestRequest.java @@ -106,6 +106,7 @@ public static CloseableHttpResponse execute( * @param includeRequestGuid whether to include request_guid parameter * @param retryHTTP403 whether to retry on HTTP 403 or not * @param noRetry should we disable retry on non-successful http resp code + * @param execTimeData ExecTimeTelemetryData * @return HttpResponse Object get from server * @throws net.snowflake.client.jdbc.SnowflakeSQLException Request timeout Exception or Illegal * State Exception i.e. connection is already shutdown etc diff --git a/src/main/java/net/snowflake/client/jdbc/ResultJsonParserV2.java b/src/main/java/net/snowflake/client/jdbc/ResultJsonParserV2.java index 795cf94ff..4cc748876 100644 --- a/src/main/java/net/snowflake/client/jdbc/ResultJsonParserV2.java +++ b/src/main/java/net/snowflake/client/jdbc/ResultJsonParserV2.java @@ -59,6 +59,10 @@ public void startParsing(JsonResultChunk resultChunk, SFBaseSession session) /** * Check if the chunk has been parsed correctly. After calling this it is safe to acquire the * output data + * + * @param in byte buffer + * @param session SFBaseSession + * @throws SnowflakeSQLException if parsing fails */ public void endParsing(ByteBuffer in, SFBaseSession session) throws SnowflakeSQLException { continueParsingInternal(in, true, session); @@ -79,6 +83,9 @@ public void endParsing(ByteBuffer in, SFBaseSession session) throws SnowflakeSQL * * @param in readOnly byteBuffer backed by an array (the data to be reed is from position to * limit) + * @param session SFBaseSession + * @return int remaining number of elements in byteBuffer + * @throws SnowflakeSQLException if an error is encountered during parsing */ public int continueParsing(ByteBuffer in, SFBaseSession session) throws SnowflakeSQLException { if (state == State.UNINITIALIZED) { @@ -95,6 +102,7 @@ public int continueParsing(ByteBuffer in, SFBaseSession session) throws Snowflak /** * @param in readOnly byteBuffer backed by an array (the data is from position to limit) * @param lastData If true, this signifies this is the last data in parsing + * @param session SFBaseSession * @throws SnowflakeSQLException Will be thrown if parsing the chunk data fails */ private void continueParsingInternal(ByteBuffer in, boolean lastData, SFBaseSession session) diff --git a/src/main/java/net/snowflake/client/jdbc/SFConnectionHandler.java b/src/main/java/net/snowflake/client/jdbc/SFConnectionHandler.java index 64297ff57..959754fd9 100644 --- a/src/main/java/net/snowflake/client/jdbc/SFConnectionHandler.java +++ b/src/main/java/net/snowflake/client/jdbc/SFConnectionHandler.java @@ -17,25 +17,47 @@ public interface SFConnectionHandler { /** - * Whether this Connection supports asynchronous queries. If yes, createAsyncResultSet may be - * called. + * @return Whether this Connection supports asynchronous queries. If yes, createAsyncResultSet may + * be called. */ boolean supportsAsyncQuery(); - /** Initializes the SnowflakeConnection */ + /** + * Initializes the SnowflakeConnection + * + * @param url url string + * @param info connection parameters + * @throws SQLException if any error is encountered + */ void initializeConnection(String url, Properties info) throws SQLException; - /** Gets the SFBaseSession implementation for this connection implementation */ + /** + * @return Gets the SFBaseSession implementation for this connection implementation + */ SFBaseSession getSFSession(); - /** Returns the SFStatementInterface implementation for this connection implementation */ + /** + * @return Returns the SFStatementInterface implementation for this connection implementation + * @throws SQLException if any error occurs + */ SFBaseStatement getSFStatement() throws SQLException; - /** Creates a result set from a query id. */ + /** + * Creates a result set from a query id. + * + * @param queryID the query ID + * @param statement Statement object + * @return ResultSet + * @throws SQLException if any error occurs + */ ResultSet createResultSet(String queryID, Statement statement) throws SQLException; /** - * Creates a SnowflakeResultSet from a base SFBaseResultSet for this connection implementation. + * @param resultSet SFBaseResultSet + * @param statement Statement + * @return Creates a SnowflakeResultSet from a base SFBaseResultSet for this connection + * implementation. + * @throws SQLException if an error occurs */ SnowflakeBaseResultSet createResultSet(SFBaseResultSet resultSet, Statement statement) throws SQLException; @@ -43,6 +65,11 @@ SnowflakeBaseResultSet createResultSet(SFBaseResultSet resultSet, Statement stat /** * Creates an asynchronous result set from a base SFBaseResultSet for this connection * implementation. + * + * @param resultSet SFBaseResultSet + * @param statement Statement + * @return An asynchronous result set from SFBaseResultSet + * @throws SQLException if an error occurs */ SnowflakeBaseResultSet createAsyncResultSet(SFBaseResultSet resultSet, Statement statement) throws SQLException; @@ -50,6 +77,9 @@ SnowflakeBaseResultSet createAsyncResultSet(SFBaseResultSet resultSet, Statement /** * @param command The command to parse for this file transfer (e.g., PUT/GET) * @param statement The statement to use for this file transfer + * @return SFBaseFileTransferAgent + * @throws SQLNonTransientConnectionException if a connection error occurs + * @throws SnowflakeSQLException if any other exception occurs */ SFBaseFileTransferAgent getFileTransferAgent(String command, SFBaseStatement statement) throws SQLNonTransientConnectionException, SnowflakeSQLException; diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java index d9149412e..ced00e325 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java @@ -93,6 +93,7 @@ private static SFBaseSession maybeGetSession(Statement statement) { * * @param resultSetSerializable The result set serializable object which includes all metadata to * create the result set + * @throws SQLException if an error occurs */ public SnowflakeBaseResultSet(SnowflakeResultSetSerializableV1 resultSetSerializable) throws SQLException { @@ -108,7 +109,7 @@ public SnowflakeBaseResultSet(SnowflakeResultSetSerializableV1 resultSetSerializ /** * This should never be used. Simply needed this for SFAsynchronousResult subclass * - * @throws SQLException + * @throws SQLException if an error occurs */ protected SnowflakeBaseResultSet() throws SQLException { this.resultSetType = 0; @@ -139,6 +140,14 @@ protected void raiseSQLExceptionIfResultSetIsClosed() throws SQLException { @Override public abstract byte[] getBytes(int columnIndex) throws SQLException; + /** + * Get Date value + * + * @param columnIndex column index + * @param tz timezone + * @return Date value at column index + * @throws SQLException if data at column index is incompatible with Date type + */ public abstract Date getDate(int columnIndex, TimeZone tz) throws SQLException; private boolean getGetDateUseNullTimezone() { @@ -168,6 +177,14 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { return getTimestamp(columnIndex, (TimeZone) null); } + /** + * Get timestamp value + * + * @param columnIndex column index + * @param tz timezone + * @return timestamp value at column index + * @throws SQLException if data at column index is incompatible with timestamp + */ public abstract Timestamp getTimestamp(int columnIndex, TimeZone tz) throws SQLException; @Override diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeChunkDownloader.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeChunkDownloader.java index 8f29f5702..fe1880083 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeChunkDownloader.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeChunkDownloader.java @@ -212,6 +212,7 @@ public void uncaughtException(Thread t, Throwable e) { * * @param resultSetSerializable the result set serializable object which includes required * metadata to start chunk downloader + * @throws SnowflakeSQLException if an error is encountered */ public SnowflakeChunkDownloader(SnowflakeResultSetSerializableV1 resultSetSerializable) throws SnowflakeSQLException { diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeColumn.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeColumn.java index 10f06dafa..13bad3195 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeColumn.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeColumn.java @@ -13,21 +13,21 @@ /** * (Optional) The name for a column in database, * - *

The default value is empty string. Provided name can override SqlData field name + * @return The default value is empty string. Provided name can override SqlData field name. */ String name() default ""; /** * (Optional) The snowflake type for a column * - *

The default value is empty string Provided type can override default type + * @return The default value is empty string Provided type can override default type. */ String type() default ""; /** * (Optional) The snowflake nullable flag for a column * - *

The default value is true Provided value can override default nullable value + * @return The default value is true Provided value can override default nullable value. */ boolean nullable() default true; @@ -37,7 +37,8 @@ * *

Applies only to columns of exact varchar and binary type. * - *

The default value {@code -1} indicates that a provider-determined length should be inferred. + * @return The default value {@code -1} indicates that a provider-determined length should be + * inferred. */ int length() default -1; /** @@ -46,8 +47,8 @@ * *

Applies only to columns of exact varchar and binary type. * - *

The default value {@code -1} indicates that a provider-determined byteLength should be - * inferred. + * @return The default value {@code -1} indicates that a provider-determined byteLength should be + * inferred. */ int byteLength() default -1; @@ -57,8 +58,8 @@ * *

Applies only to columns of exact numeric type. * - *

The default value {@code -1} indicates that a provider-determined precision should be - * inferred. + * @return The default value {@code -1} indicates that a provider-determined precision should be + * inferred. */ int precision() default -1; @@ -68,7 +69,8 @@ * *

Applies only to columns of exact numeric type. * - *

The default value {@code 0} indicates that a provider-determined scale should be inferred. + * @return The default value {@code 0} indicates that a provider-determined scale should be + * inferred. */ int scale() default -1; } diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeColumnMetadata.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeColumnMetadata.java index 4525c2efb..69f467b90 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeColumnMetadata.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeColumnMetadata.java @@ -76,6 +76,19 @@ public SnowflakeColumnMetadata( * @deprecated Use {@link SnowflakeColumnMetadata#SnowflakeColumnMetadata(String, int, boolean, * int, int, int, String, boolean, SnowflakeType, List, String, String, String, boolean, int)} * instead + * @param name name + * @param type type + * @param nullable is nullable + * @param length length + * @param precision precision + * @param scale scale + * @param typeName type name + * @param fixed is fixed + * @param base SnowflakeType + * @param columnSrcDatabase column source database + * @param columnSrcSchema column source schema + * @param columnSrcTable column source table + * @param isAutoIncrement is auto-increment */ @Deprecated public SnowflakeColumnMetadata( diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeConnection.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeConnection.java index e997b053e..6edc510f8 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeConnection.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeConnection.java @@ -47,7 +47,7 @@ InputStream downloadStream(String stageName, String sourceFileName, boolean deco * Return unique session ID from current session generated by making connection * * @return a unique alphanumeric value representing current session ID - * @throws SQLException + * @throws SQLException if an error occurs */ String getSessionID() throws SQLException; @@ -56,12 +56,16 @@ InputStream downloadStream(String stageName, String sourceFileName, boolean deco * of corresponding query. Used when original ResultSet object is no longer available, such as * when original connection has been closed. * - * @param queryID - * @return - * @throws SQLException + * @param queryID the query ID + * @return ResultSet based off the query ID + * @throws SQLException if an error occurs */ ResultSet createResultSet(String queryID) throws SQLException; - /** Returns the SnowflakeConnectionImpl from the connection object. */ + /** + * Returns the SnowflakeConnectionImpl from the connection object. + * + * @return SFConnectionHandler + */ SFConnectionHandler getHandler(); } diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectionV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectionV1.java index 498e6393b..1f55c83f4 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectionV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeConnectionV1.java @@ -91,6 +91,7 @@ public class SnowflakeConnectionV1 implements Connection, SnowflakeConnection { * Instantiates a SnowflakeConnectionV1 with the passed-in SnowflakeConnectionImpl. * * @param sfConnectionHandler The SnowflakeConnectionImpl. + * @throws SQLException if failed to instantiate a SnowflakeConnectionV1. */ public SnowflakeConnectionV1(SFConnectionHandler sfConnectionHandler) throws SQLException { initConnectionWithImpl(sfConnectionHandler, null, null); @@ -100,6 +101,9 @@ public SnowflakeConnectionV1(SFConnectionHandler sfConnectionHandler) throws SQL * Instantiates a SnowflakeConnectionV1 with the passed-in SnowflakeConnectionImpl. * * @param sfConnectionHandler The SnowflakeConnectionImpl. + * @param url The URL string. + * @param info Connection properties. + * @throws SQLException if failed to instantiate connection. */ public SnowflakeConnectionV1(SFConnectionHandler sfConnectionHandler, String url, Properties info) throws SQLException { @@ -195,9 +199,9 @@ public Statement createStatement() throws SQLException { /** * Get an instance of a ResultSet object * - * @param queryID - * @return - * @throws SQLException + * @param queryID the query ID + * @return ResultSet + * @throws SQLException if connection is closed */ public ResultSet createResultSet(String queryID) throws SQLException { raiseSQLExceptionIfConnectionIsClosed(); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java index b385e04c2..298b64ee7 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeDriver.java @@ -167,7 +167,7 @@ public static String getDisableArrowResultFormatMessage() { /** * Utility method to verify if the standard or fips snowflake-jdbc driver is being used. * - * @return + * @return the title of the implementation, null is returned if it is not known. */ public static String getImplementationTitle() { Package pkg = Package.getPackage("net.snowflake.client.jdbc"); @@ -177,7 +177,7 @@ public static String getImplementationTitle() { /** * Utility method to get the complete jar name with version. * - * @return + * @return the jar name with version */ public static String getJdbcJarname() { return String.format("%s-%s", getImplementationTitle(), implementVersion); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java index ce289e90b..2b660eb08 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java @@ -791,6 +791,7 @@ public static Callable getDownloadFileCallable( * @param encMat remote store encryption material * @param parallel number of parallel threads for downloading * @param presignedUrl Presigned URL for file download + * @param queryId the query ID * @return a callable responsible for downloading files */ public static Callable getDownloadFileCallable( @@ -3373,7 +3374,7 @@ public static void throwJCEMissingError(String operation, Exception ex, String q * @param session the current session * @param operation the operation i.e. GET * @param ex the exception caught - * @throws SnowflakeSQLLoggedException + * @throws SnowflakeSQLLoggedException if not enough space left on device to download file. */ @Deprecated public static void throwNoSpaceLeftError(SFSession session, String operation, Exception ex) @@ -3388,7 +3389,8 @@ public static void throwNoSpaceLeftError(SFSession session, String operation, Ex * @param session the current session * @param operation the operation i.e. GET * @param ex the exception caught - * @throws SnowflakeSQLLoggedException + * @param queryId the query ID + * @throws SnowflakeSQLLoggedException if not enough space left on device to download file. */ public static void throwNoSpaceLeftError( SFSession session, String operation, Exception ex, String queryId) diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferConfig.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferConfig.java index 60ca632ad..438abb4b2 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferConfig.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferConfig.java @@ -190,7 +190,12 @@ public Builder setUseS3RegionalUrl(boolean useS3RegUrl) { return this; } - /** Streaming ingest client name, used to calculate streaming ingest billing per client */ + /** + * Streaming ingest client name, used to calculate streaming ingest billing per client + * + * @param streamingIngestClientName streaming ingest client name + * @return Builder + */ public Builder setStreamingIngestClientName(String streamingIngestClientName) { this.streamingIngestClientName = streamingIngestClientName; return this; @@ -199,6 +204,9 @@ public Builder setStreamingIngestClientName(String streamingIngestClientName) { /** * Streaming ingest client key provided by Snowflake, used to calculate streaming ingest billing * per client + * + * @param streamingIngestClientKey streaming ingest client key + * @return Builder */ public Builder setStreamingIngestClientKey(String streamingIngestClientKey) { this.streamingIngestClientKey = streamingIngestClientKey; diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java index ee3dc3ec8..2f7ec66f4 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java @@ -8,6 +8,7 @@ public interface SnowflakePreparedStatement { /** * @return the Snowflake query ID of the latest executed query + * @throws SQLException if an error occurs */ String getQueryID() throws SQLException; @@ -15,25 +16,27 @@ public interface SnowflakePreparedStatement { * Execute a query asynchronously * * @return ResultSet containing results - * @throws SQLException + * @throws SQLException if an error occurs */ ResultSet executeAsyncQuery() throws SQLException; /** * Sets the designated parameter to the given BigInteger value. * - * @param parameterIndex - * @param x - * @throws SQLException + * @param parameterIndex the parameter index + * @param x the BigInteger value + * @throws SQLException if an error occurs */ void setBigInteger(int parameterIndex, BigInteger x) throws SQLException; /** * Sets the designated parameter to the given Map instance. * - * @param parameterIndex - * @param map - * @throws SQLException + * @param parameterIndex the parameter index + * @param map the map instance + * @param type the type + * @param generic type + * @throws SQLException if an error occurs */ void setMap(int parameterIndex, Map map, int type) throws SQLException; } diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSet.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSet.java index 1fc7ff9ee..2df8975b5 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSet.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSet.java @@ -12,6 +12,7 @@ public interface SnowflakeResultSet { /** * @return the Snowflake query ID of the query which generated this result set + * @throws SQLException if an error is encountered */ String getQueryID() throws SQLException; @@ -23,7 +24,7 @@ public interface SnowflakeResultSet { * query statuses. QueryStatus = SUCCESS means results can be retrieved. * * @return QueryStatus enum showing status of query - * @throws SQLException + * @throws SQLException if an error is encountered */ QueryStatus getStatus() throws SQLException; @@ -33,7 +34,7 @@ public interface SnowflakeResultSet { * returned. * * @return String value of query's error message - * @throws SQLException + * @throws SQLException if an error is encountered */ String getQueryErrorMessage() throws SQLException; @@ -45,7 +46,7 @@ public interface SnowflakeResultSet { *

status.isSuccess() means that results can be retrieved. * * @return an instance containing query metadata - * @throws SQLException + * @throws SQLException if an error is encountered */ QueryStatusV2 getStatusV2() throws SQLException; diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializable.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializable.java index f5a9aa97c..2a1ba82a1 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializable.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializable.java @@ -71,6 +71,7 @@ public Builder setSfFullURL(String sfFullURL) { * sc:2.8.2/jdbc:3.12.12 since Sept 2020. It is safe to remove it after Sept 2022. * * @return a ResultSet which represents for the data wrapped in the object + * @throws SQLException if an error occurs * @deprecated Use {@link #getResultSet(ResultSetRetrieveConfig)} instead */ @Deprecated @@ -84,6 +85,7 @@ public Builder setSfFullURL(String sfFullURL) { * * @param info The proxy server information if proxy is necessary. * @return a ResultSet which represents for the data wrapped in the object + * @throws SQLException if an error occurs * @deprecated Use {@link #getResultSet(ResultSetRetrieveConfig)} instead */ @Deprecated @@ -94,6 +96,7 @@ public Builder setSfFullURL(String sfFullURL) { * * @param resultSetRetrieveConfig The extra info to retrieve the result set. * @return a ResultSet which represents for the data wrapped in the object + * @throws SQLException if an error occurs */ ResultSet getResultSet(ResultSetRetrieveConfig resultSetRetrieveConfig) throws SQLException; @@ -101,6 +104,7 @@ public Builder setSfFullURL(String sfFullURL) { * Retrieve total row count included in the ResultSet Serializable object. * * @return the total row count from metadata + * @throws SQLException if an error occurs */ long getRowCount() throws SQLException; @@ -108,6 +112,7 @@ public Builder setSfFullURL(String sfFullURL) { * Retrieve compressed data size included in the ResultSet Serializable object. * * @return the total compressed data size in bytes from metadata + * @throws SQLException if an error occurs */ long getCompressedDataSizeInBytes() throws SQLException; @@ -115,6 +120,7 @@ public Builder setSfFullURL(String sfFullURL) { * Retrieve uncompressed data size included in the ResultSet Serializable object. * * @return the total uncompressed data size in bytes from metadata + * @throws SQLException if an error occurs */ long getUncompressedDataSizeInBytes() throws SQLException; } diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableV1.java index 082dc2e30..2baf8027a 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableV1.java @@ -282,6 +282,7 @@ private SnowflakeResultSetSerializableV1(SnowflakeResultSetSerializableV1 toCopy * @param sfStatement the Snowflake statement * @param resultStreamProvider a ResultStreamProvider for computing a custom data source for * result-file streams + * @param disableChunksPrefetch is prefetch disabled * @throws SnowflakeSQLException if failed to parse the result JSON node */ protected SnowflakeResultSetSerializableV1( @@ -754,6 +755,12 @@ public static SnowflakeResultSetSerializableV1 create( /** * A factory function for internal usage only. It creates SnowflakeResultSetSerializableV1 with * NoOpChunksDownloader which disables chunks prefetch. + * + * @param rootNode JSON root node + * @param sfSession SFBaseSession + * @param sfStatement SFBaseStatement + * @return SnowflakeResultSetSerializableV1 with NoOpChunksDownloader + * @throws SnowflakeSQLException if an error occurs */ @SnowflakeJdbcInternalApi public static SnowflakeResultSetSerializableV1 createWithChunksPrefetchDisabled( diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetV1.java index 49c8c8546..4f73b4c18 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeResultSetV1.java @@ -64,7 +64,7 @@ public SnowflakeResultSetV1(SFBaseResultSet sfBaseResultSet, Statement statement * This function is not supported for synchronous queries * * @return no return value; exception is always thrown - * @throws SQLFeatureNotSupportedException + * @throws SQLFeatureNotSupportedException always thrown because feature is not supported */ public QueryStatus getStatus() throws SQLException { throw new SnowflakeLoggedFeatureNotSupportedException(session); @@ -74,7 +74,7 @@ public QueryStatus getStatus() throws SQLException { * This function is not supported for synchronous queries * * @return no return value; exception is always thrown - * @throws SQLFeatureNotSupportedException + * @throws SQLFeatureNotSupportedException always thrown because feature is not supported */ @Override public QueryStatusV2 getStatusV2() throws SQLException { @@ -86,7 +86,7 @@ public QueryStatusV2 getStatusV2() throws SQLException { * This function is not supported for synchronous queries * * @return no return value; exception is always thrown - * @throws SQLFeatureNotSupportedException + * @throws SQLFeatureNotSupportedException always thrown because feature is not supported */ @Override public String getQueryErrorMessage() throws SQLException { diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeRichResultSetSerializableV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeRichResultSetSerializableV1.java index 194748317..084e62ba8 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeRichResultSetSerializableV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeRichResultSetSerializableV1.java @@ -38,6 +38,12 @@ public class SnowflakeRichResultSetSerializableV1 extends SnowflakeResultSetSeri /** * A factory function for internal usage only. It creates SnowflakeRichResultSetSerializableV1 * with NoOpChunksDownloader which disables chunks prefetch. + * + * @param rootNode JSON root node + * @param sfSession SFBaseSession + * @param sfStatement SFBaseStatement + * @return SnowflakeRichResultSetSerializableV1 with NoOpChunksDownloader + * @throws SnowflakeSQLException if an error occurs */ public static SnowflakeRichResultSetSerializableV1 createWithChunksPrefetchDisabled( JsonNode rootNode, SFBaseSession sfSession, SFBaseStatement sfStatement) diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeSQLException.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeSQLException.java index ebe84c13c..48faec24c 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeSQLException.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeSQLException.java @@ -51,12 +51,22 @@ public SnowflakeSQLException(String queryId, String reason, String sqlState, int queryId); } - /** use {@link SnowflakeSQLException#SnowflakeSQLException(String, String, String)} */ + /** + * use {@link SnowflakeSQLException#SnowflakeSQLException(String, String, String)} + * + * @param reason exception reason + * @param sqlState the SQL state + */ @Deprecated public SnowflakeSQLException(String reason, String sqlState) { this((String) null, reason, sqlState); } + /** + * @param queryId the queryID + * @param reason exception reason + * @param sqlState the SQL state + */ public SnowflakeSQLException(String queryId, String reason, String sqlState) { super(reason, sqlState); this.queryId = queryId; @@ -64,12 +74,22 @@ public SnowflakeSQLException(String queryId, String reason, String sqlState) { logger.debug("Snowflake exception: {}, sqlState:{}", reason, sqlState); } - /** use {@link SnowflakeSQLException#SnowflakeSQLException(String, String, int)} */ + /** + * use {@link SnowflakeSQLException#SnowflakeSQLException(String, String, int)} + * + * @param sqlState the SQL state + * @param vendorCode the vendor code + */ @Deprecated public SnowflakeSQLException(String sqlState, int vendorCode) { this((String) null, sqlState, vendorCode); } + /** + * @param queryId query ID + * @param sqlState SQL state + * @param vendorCode vendor code + */ public SnowflakeSQLException(String queryId, String sqlState, int vendorCode) { super( errorResourceBundleManager.getLocalizedMessage(String.valueOf(vendorCode)), @@ -83,12 +103,24 @@ public SnowflakeSQLException(String queryId, String sqlState, int vendorCode) { vendorCode); } - /** use {@link SnowflakeSQLException#SnowflakeSQLException(String, String, int, Object...)} */ + /** + * use {@link SnowflakeSQLException#SnowflakeSQLException(String, String, int, Object...)} + * + * @param sqlState the SQL state + * @param vendorCode the vendor code + * @param params additional parameters + */ @Deprecated public SnowflakeSQLException(String sqlState, int vendorCode, Object... params) { this((String) null, sqlState, vendorCode, params); } + /** + * @param queryId query ID + * @param sqlState the SQL state + * @param vendorCode the vendor code + * @param params additional parameters + */ public SnowflakeSQLException(String queryId, String sqlState, int vendorCode, Object... params) { super( errorResourceBundleManager.getLocalizedMessage(String.valueOf(vendorCode), params), @@ -102,6 +134,11 @@ public SnowflakeSQLException(String queryId, String sqlState, int vendorCode, Ob vendorCode); } + /** + * @param ex Throwable exception + * @param sqlState the SQL state + * @param vendorCode the vendor code + */ public SnowflakeSQLException(Throwable ex, String sqlState, int vendorCode) { super( errorResourceBundleManager.getLocalizedMessage(String.valueOf(vendorCode)), @@ -115,6 +152,11 @@ public SnowflakeSQLException(Throwable ex, String sqlState, int vendorCode) { ex); } + /** + * @param ex Throwable exception + * @param errorCode the error code + * @param params additional parameters + */ public SnowflakeSQLException(Throwable ex, ErrorCode errorCode, Object... params) { this(ex, errorCode.getSqlState(), errorCode.getMessageCode(), params); } @@ -122,12 +164,23 @@ public SnowflakeSQLException(Throwable ex, ErrorCode errorCode, Object... params /** * @deprecated use {@link SnowflakeSQLException#SnowflakeSQLException(String, Throwable, String, * int, Object...)} + * @param ex Throwable exception + * @param sqlState the SQL state + * @param vendorCode the vendor code + * @param params additional parameters */ @Deprecated public SnowflakeSQLException(Throwable ex, String sqlState, int vendorCode, Object... params) { this(null, ex, sqlState, vendorCode, params); } + /** + * @param queryId query ID + * @param ex Throwable exception + * @param sqlState the SQL state + * @param vendorCode the vendor code + * @param params additional parameters + */ public SnowflakeSQLException( String queryId, Throwable ex, String sqlState, int vendorCode, Object... params) { super( @@ -143,6 +196,10 @@ public SnowflakeSQLException( ex); } + /** + * @param errorCode the error code + * @param params additional parameters + */ public SnowflakeSQLException(ErrorCode errorCode, Object... params) { super( errorResourceBundleManager.getLocalizedMessage( @@ -151,6 +208,11 @@ public SnowflakeSQLException(ErrorCode errorCode, Object... params) { errorCode.getMessageCode()); } + /** + * @param queryId query ID + * @param errorCode error code + * @param params additional parameters + */ public SnowflakeSQLException(String queryId, ErrorCode errorCode, Object... params) { super( errorResourceBundleManager.getLocalizedMessage( @@ -160,6 +222,12 @@ public SnowflakeSQLException(String queryId, ErrorCode errorCode, Object... para this.queryId = queryId; } + /** + * @param errorCode error code + * @param retryCount retry count + * @param issocketTimeoutNoBackoff issocketTimeoutNoBackoff + * @param elapsedSeconds time elapsed in seconds + */ public SnowflakeSQLException( ErrorCode errorCode, int retryCount, boolean issocketTimeoutNoBackoff, long elapsedSeconds) { super( @@ -171,6 +239,9 @@ public SnowflakeSQLException( this.elapsedSeconds = elapsedSeconds; } + /** + * @param e the SFException + */ public SnowflakeSQLException(SFException e) { this(e.getQueryId(), e.getMessage(), e.getSqlState(), e.getVendorCode()); } diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeSQLLoggedException.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeSQLLoggedException.java index 9f8f2d7a9..78d4fb971 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeSQLLoggedException.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeSQLLoggedException.java @@ -100,7 +100,7 @@ private static Future sendInBandTelemetryMessage( * Helper function to remove sensitive data (error message, reason) from the stacktrace. * * @param stackTrace original stacktrace - * @return + * @return stack trace with sensitive data removed */ static String maskStacktrace(String stackTrace) { Pattern STACKTRACE_BEGINNING = @@ -118,9 +118,9 @@ static String maskStacktrace(String stackTrace) { /** * Helper function to create JSONObject node for OOB telemetry log * - * @param queryId - * @param SQLState - * @param vendorCode + * @param queryId query ID + * @param SQLState the SQL state + * @param vendorCode the vendor code * @return JSONObject with data about SQLException */ static JSONObject createOOBValue(String queryId, String SQLState, int vendorCode) { @@ -143,10 +143,10 @@ static JSONObject createOOBValue(String queryId, String SQLState, int vendorCode /** * Helper function to create ObjectNode for IB telemetry log * - * @param queryId - * @param SQLState - * @param vendorCode - * @return + * @param queryId query ID + * @param SQLState the SQL state + * @param vendorCode the vendor code + * @return ObjectNode for IB telemetry log */ static ObjectNode createIBValue(String queryId, String SQLState, int vendorCode) { ObjectNode ibValue = mapper.createObjectNode(); @@ -224,17 +224,35 @@ public static void sendTelemetryData( } } + /** + * @param session SFBaseSession + * @param reason exception reason + * @param SQLState the SQL state + * @param vendorCode the vendor code + * @param queryId the query ID + */ public SnowflakeSQLLoggedException( SFBaseSession session, String reason, String SQLState, int vendorCode, String queryId) { super(queryId, reason, SQLState, vendorCode); sendTelemetryData(queryId, SQLState, vendorCode, session, this); } + /** + * @param session SFBaseSession + * @param vendorCode the vendor code + * @param SQLState the SQL state + */ public SnowflakeSQLLoggedException(SFBaseSession session, int vendorCode, String SQLState) { super(SQLState, vendorCode); sendTelemetryData(null, SQLState, vendorCode, session, this); } + /** + * @param queryId the query ID + * @param session SFBaseSession + * @param vendorCode the vendor code + * @param SQLState the SQL state + */ public SnowflakeSQLLoggedException( String queryId, SFBaseSession session, int vendorCode, String SQLState) { super(queryId, SQLState, vendorCode); @@ -244,41 +262,85 @@ public SnowflakeSQLLoggedException( /** * use {@link SnowflakeSQLLoggedException#SnowflakeSQLLoggedException(String, SFBaseSession, * String, String)} + * + * @param session SFBaseSession + * @param SQLState the SQL state + * @param reason exception reason */ @Deprecated public SnowflakeSQLLoggedException(SFBaseSession session, String SQLState, String reason) { this(null, session, SQLState, reason); } + /** + * @param queryId the query ID + * @param session SFBaseSession + * @param SQLState the SQL state + * @param reason the exception reason + */ public SnowflakeSQLLoggedException( String queryId, SFBaseSession session, String SQLState, String reason) { super(reason, SQLState); sendTelemetryData(queryId, SQLState, -1, session, this); } + /** + * @param session SFBaseSession + * @param vendorCode the vendor code + * @param SQLState the SQL state + * @param params additional parameters + */ public SnowflakeSQLLoggedException( SFBaseSession session, int vendorCode, String SQLState, Object... params) { this(null, session, vendorCode, SQLState, params); } + /** + * @param queryId the query ID + * @param session SFBaseSession + * @param vendorCode the vendor code + * @param SQLState the SQL state + * @param params additional parameters + */ public SnowflakeSQLLoggedException( String queryId, SFBaseSession session, int vendorCode, String SQLState, Object... params) { super(queryId, SQLState, vendorCode, params); sendTelemetryData(queryId, SQLState, vendorCode, session, this); } + /** + * @param session SFBaseSession + * @param errorCode the error code + * @param ex Throwable exception + * @param params additional parameters + */ public SnowflakeSQLLoggedException( SFBaseSession session, ErrorCode errorCode, Throwable ex, Object... params) { super(ex, errorCode, params); sendTelemetryData(null, errorCode.getSqlState(), errorCode.getMessageCode(), session, this); } + /** + * @param session SFBaseSession + * @param SQLState the SQL state + * @param vendorCode the vendor code + * @param ex Throwable exception + * @param params additional parameters + */ public SnowflakeSQLLoggedException( SFBaseSession session, String SQLState, int vendorCode, Throwable ex, Object... params) { super(ex, SQLState, vendorCode, params); sendTelemetryData(null, SQLState, vendorCode, session, this); } + /** + * @param queryId the query ID + * @param session SFBaseSession + * @param SQLState the SQL state + * @param vendorCode the vendor code + * @param ex Throwable exception + * @param params additional parameters + */ public SnowflakeSQLLoggedException( String queryId, SFBaseSession session, @@ -293,18 +355,32 @@ public SnowflakeSQLLoggedException( /** * use {@link SnowflakeSQLLoggedException#SnowflakeSQLLoggedException(String, SFBaseSession, * ErrorCode, Object...)} + * + * @param session SFBaseSession + * @param errorCode the error code + * @param params additional parameters */ @Deprecated public SnowflakeSQLLoggedException(SFBaseSession session, ErrorCode errorCode, Object... params) { this(null, session, errorCode, params); } + /** + * @param queryId the query ID + * @param session SFBaseSession + * @param errorCode the error code + * @param params additional parameters + */ public SnowflakeSQLLoggedException( String queryId, SFBaseSession session, ErrorCode errorCode, Object... params) { super(queryId, errorCode, params); sendTelemetryData(queryId, null, -1, session, this); } + /** + * @param session SFBaseSession + * @param e throwable exception + */ public SnowflakeSQLLoggedException(SFBaseSession session, SFException e) { super(e); sendTelemetryData(null, null, -1, session, this); @@ -313,12 +389,20 @@ public SnowflakeSQLLoggedException(SFBaseSession session, SFException e) { /** * use {@link SnowflakeSQLLoggedException#SnowflakeSQLLoggedException(String, SFBaseSession, * String)} + * + * @param session SFBaseSession + * @param reason exception reason */ @Deprecated public SnowflakeSQLLoggedException(SFBaseSession session, String reason) { this(null, session, reason); } + /** + * @param queryId the query ID + * @param session SFBaseSession + * @param reason exception reason + */ public SnowflakeSQLLoggedException(String queryId, SFBaseSession session, String reason) { super(queryId, reason, null); sendTelemetryData(queryId, null, -1, session, this); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeStatement.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeStatement.java index f1f41d4d0..d684c3d27 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeStatement.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeStatement.java @@ -14,11 +14,13 @@ public interface SnowflakeStatement { /** * @return the Snowflake query ID of the latest executed query (even failed one) or null when the * last query ID is not available + * @throws SQLException if an error is encountered */ String getQueryID() throws SQLException; /** * @return the Snowflake query IDs of the latest executed batch queries + * @throws SQLException if an error is encountered */ List getBatchQueryIDs() throws SQLException; @@ -27,9 +29,15 @@ public interface SnowflakeStatement { * * @param name parameter name * @param value parameter value + * @throws SQLException if an error is encountered */ void setParameter(String name, Object value) throws SQLException; + /** + * Set batch ID + * + * @param batchID the batch ID + */ void setBatchID(String batchID); /** @@ -46,8 +54,8 @@ public interface SnowflakeStatement { * required as SnowflakeStatementV1 doesn't directly expose ResultSet to the sub-classes making it * challenging to get additional information from the previously executed query. * - * @param resultSet - * @throws SQLException + * @param resultSet SFBaseResultSet + * @throws SQLException if an error is encountered */ void resultSetMetadataHandler(SFBaseResultSet resultSet) throws SQLException; } diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeType.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeType.java index ea958c551..5e59dcbc4 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeType.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeType.java @@ -102,7 +102,12 @@ public static JavaDataType getJavaType(SnowflakeType type, boolean isStructuredT } } - /** Converts text of data type (returned from SQL query) into Types type, represented by an int */ + /** + * Converts text of data type (returned from SQL query) into Types type, represented by an int + * + * @param typeName type name + * @return int representation of type + */ public static int convertStringToType(String typeName) { int retval = Types.NULL; if (typeName == null || typeName.trim().isEmpty()) { diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index 05dad6292..635384972 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -176,7 +176,15 @@ private static void checkErrorAndThrowExceptionSub( throw new SnowflakeSQLException(queryId, errorMessage, sqlState, errorCode); } - /** This method should only be used internally */ + /** + * This method should only be used internally + * + * @param colNode JsonNode + * @param jdbcTreatDecimalAsInt true if should treat Decimal as Int + * @param session SFBaseSession + * @return SnowflakeColumnMetadata + * @throws SnowflakeSQLException if an error occurs + */ @Deprecated public static SnowflakeColumnMetadata extractColumnMetadata( JsonNode colNode, boolean jdbcTreatDecimalAsInt, SFBaseSession session) @@ -665,7 +673,12 @@ public static String systemGetEnv(String env) { return null; } - /** System.setEnv function. Can be used for unit tests. */ + /** + * System.setEnv function. Can be used for unit tests. + * + * @param key key + * @param value value + */ public static void systemSetEnv(String key, String value) { try { Map env = System.getenv(); @@ -696,7 +709,7 @@ public static void systemSetEnv(String key, String value) { /** * System.unsetEnv function to remove a system environment parameter in the map * - * @param key + * @param key key value */ public static void systemUnsetEnv(String key) { try { @@ -718,6 +731,8 @@ public static void systemUnsetEnv(String key) { * * @param mode OCSP mode * @param info proxy server properties. + * @return HttpClientSettingsKey + * @throws SnowflakeSQLException if an error occurs */ public static HttpClientSettingsKey convertProxyPropertiesToHttpClientKey( OCSPMode mode, Properties info) throws SnowflakeSQLException { @@ -773,8 +788,8 @@ public static HttpClientSettingsKey convertProxyPropertiesToHttpClientKey( * SimpleDateFormatter. Negative values have to be rounded to the next negative value, while * positive values should be cut off with no rounding. * - * @param millis - * @return + * @param millis milliseconds + * @return seconds as long value */ public static long getSecondsFromMillis(long millis) { long returnVal; @@ -843,6 +858,9 @@ public static String getJsonNodeStringValue(JsonNode node) throws SFException { /** * Method introduced to avoid inconsistencies in custom headers handling, since these are defined * on drivers side e.g. some drivers might internally convert headers to canonical form. + * + * @param input map input + * @return case insensitive map */ @SnowflakeJdbcInternalApi public static Map createCaseInsensitiveMap(Map input) { @@ -853,7 +871,12 @@ public static Map createCaseInsensitiveMap(Map i return caseInsensitiveMap; } - /** toCaseInsensitiveMap, but adjusted to Headers[] argument type */ + /** + * toCaseInsensitiveMap, but adjusted to Headers[] argument type + * + * @param headers array of headers + * @return case insensitive map + */ @SnowflakeJdbcInternalApi public static Map createCaseInsensitiveMap(Header[] headers) { if (headers != null) { diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/EncryptionProvider.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/EncryptionProvider.java index d9999457d..7acb2fc4a 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/EncryptionProvider.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/EncryptionProvider.java @@ -43,7 +43,22 @@ public class EncryptionProvider { private static final int BUFFER_SIZE = 2 * 1024 * 1024; // 2 MB private static SecureRandom secRnd; - /** Decrypt a InputStream */ + /** + * Decrypt a InputStream + * + * @param inputStream input stream + * @param keyBase64 keyBase64 + * @param ivBase64 ivBase64 + * @param encMat RemoteStoreFileEncryptionMaterial + * @return InputStream + * @throws NoSuchPaddingException when padding mechanism is not available for this environment + * @throws NoSuchAlgorithmException when the requested algorithm is not available for this + * environment + * @throws InvalidKeyException when there is an issue with the key value + * @throws BadPaddingException when the data is not padded as expected + * @throws IllegalBlockSizeException when the length of data is incorrect + * @throws InvalidAlgorithmParameterException when the provided KeyStore has no trustAnchors + */ public static InputStream decryptStream( InputStream inputStream, String keyBase64, diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3HttpUtil.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3HttpUtil.java index 49b3542fd..565db0210 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3HttpUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/S3HttpUtil.java @@ -67,7 +67,7 @@ public static void setProxyForS3(HttpClientSettingsKey key, ClientConfiguration * * @param proxyProperties proxy properties * @param clientConfig the configuration needed by S3 to set the proxy - * @throws SnowflakeSQLException + * @throws SnowflakeSQLException when an error is encountered */ public static void setSessionlessProxyForS3( Properties proxyProperties, ClientConfiguration clientConfig) throws SnowflakeSQLException { diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java index 31341c5a3..188ba40d4 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java @@ -186,8 +186,8 @@ public void shutdown() { * * @param remoteStorageLocation bucket name * @param prefix Path - * @return - * @throws StorageProviderException + * @return a collection of storage summary objects + * @throws StorageProviderException cloud storage provider error */ @Override public StorageObjectSummaryCollection listObjects(String remoteStorageLocation, String prefix) @@ -1371,13 +1371,11 @@ public void addStreamingIngestMetadata( meta.addUserMetadata(GCS_STREAMING_INGEST_CLIENT_KEY, clientKey); } - /** Gets streaming ingest client name to the StorageObjectMetadata object */ @Override public String getStreamingIngestClientName(StorageObjectMetadata meta) { return meta.getUserMetadata().get(GCS_STREAMING_INGEST_CLIENT_NAME); } - /** Gets streaming ingest client key to the StorageObjectMetadata object */ @Override public String getStreamingIngestClientKey(StorageObjectMetadata meta) { return meta.getUserMetadata().get(GCS_STREAMING_INGEST_CLIENT_KEY); diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3Client.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3Client.java index bdede5843..f1a2392bb 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3Client.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3Client.java @@ -944,7 +944,12 @@ private static void handleS3Exception( } } - /** Checks the status code of the exception to see if it's a 400 or 404 */ + /** + * Checks the status code of the exception to see if it's a 400 or 404 + * + * @param ex exception + * @return true if it's a 400 or 404 status code + */ public boolean isClientException400Or404(Exception ex) { if (ex instanceof AmazonServiceException) { AmazonServiceException asEx = (AmazonServiceException) (ex); @@ -954,13 +959,13 @@ public boolean isClientException400Or404(Exception ex) { return false; } - /** Returns the material descriptor key */ + /* Returns the material descriptor key */ @Override public String getMatdescKey() { return "x-amz-matdesc"; } - /** Adds encryption metadata to the StorageObjectMetadata object */ + /* Adds encryption metadata to the StorageObjectMetadata object */ @Override public void addEncryptionMetadata( StorageObjectMetadata meta, @@ -974,13 +979,13 @@ public void addEncryptionMetadata( meta.setContentLength(contentLength); } - /** Adds digest metadata to the StorageObjectMetadata object */ + /* Adds digest metadata to the StorageObjectMetadata object */ @Override public void addDigestMetadata(StorageObjectMetadata meta, String digest) { meta.addUserMetadata("sfc-digest", digest); } - /** Gets digest metadata to the StorageObjectMetadata object */ + /* Gets digest metadata to the StorageObjectMetadata object */ @Override public String getDigestMetadata(StorageObjectMetadata meta) { return meta.getUserMetadata().get("sfc-digest"); @@ -1005,7 +1010,7 @@ private static SSLConnectionSocketFactory getSSLConnectionSocketFactory() { return s3ConnectionSocketFactory; } - /** + /* * Adds streaming ingest metadata to the StorageObjectMetadata object, used for streaming ingest * per client billing calculation */ @@ -1016,13 +1021,11 @@ public void addStreamingIngestMetadata( meta.addUserMetadata(S3_STREAMING_INGEST_CLIENT_KEY, clientKey); } - /** Gets streaming ingest client name to the StorageObjectMetadata object */ @Override public String getStreamingIngestClientName(StorageObjectMetadata meta) { return meta.getUserMetadata().get(S3_STREAMING_INGEST_CLIENT_NAME); } - /** Gets streaming ingest client key to the StorageObjectMetadata object */ @Override public String getStreamingIngestClientKey(StorageObjectMetadata meta) { return meta.getUserMetadata().get(S3_STREAMING_INGEST_CLIENT_KEY); diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeStorageClient.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeStorageClient.java index 4be936763..ba74ac7d2 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeStorageClient.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeStorageClient.java @@ -523,9 +523,19 @@ default void addEncryptionMetadataForGcm( */ void addStreamingIngestMetadata(StorageObjectMetadata meta, String clientName, String clientKey); - /** Gets streaming ingest client name to the StorageObjectMetadata object */ + /** + * Gets streaming ingest client name to the StorageObjectMetadata object + * + * @param meta StorageObjectMetadata + * @return Client name + */ String getStreamingIngestClientName(StorageObjectMetadata meta); - /** Gets streaming ingest client key to the StorageObjectMetadata object */ + /** + * Gets streaming ingest client key to the StorageObjectMetadata object + * + * @param meta StorageObjectMetadata + * @return Client key + */ String getStreamingIngestClientKey(StorageObjectMetadata meta); } diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/StorageClientFactory.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/StorageClientFactory.java index ac7de73a6..a321b6ebd 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/StorageClientFactory.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/StorageClientFactory.java @@ -47,6 +47,7 @@ public static StorageClientFactory getFactory() { * @param stage the stage properties * @param parallel the degree of parallelism to be used by the client * @param encMat encryption material for the client + * @param session SFSession * @return a SnowflakeStorageClient interface to the instance created * @throws SnowflakeSQLException if any error occurs */ diff --git a/src/main/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryService.java b/src/main/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryService.java index ed360789e..5e163c8bf 100644 --- a/src/main/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryService.java +++ b/src/main/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryService.java @@ -158,7 +158,11 @@ public JSONObject getContext() { return context; } - /** Note: Only used for IT */ + /** + * Note: Only used for IT + * + * @param params parameter map + */ public void updateContextForIT(Map params) { Properties info = new Properties(); for (String key : params.keySet()) { @@ -247,7 +251,11 @@ private void configureDeployment(SnowflakeConnectString conStr) { this.setDeployment(deployment); } - /** whether the telemetry service is enabled for current deployment */ + /** + * whether the telemetry service is enabled for current deployment + * + * @return true if the telemetry service is enabled for current deployment + */ public boolean isDeploymentEnabled() { return ENABLED_DEPLOYMENT.contains(this.serverDeployment.name); } @@ -372,7 +380,11 @@ public void count() { eventCnt.incrementAndGet(); } - /** Report the event to the telemetry server in a new thread */ + /** + * Report the event to the telemetry server in a new thread + * + * @param event TelemetryEvent + */ public void report(TelemetryEvent event) { reportChooseEvent(event, /* isHTAP */ false); } @@ -389,7 +401,12 @@ public void reportChooseEvent(TelemetryEvent event, boolean isHTAP) { TelemetryThreadPool.getInstance().execute(runUpload); } - /** Convert an event to a payload in string */ + /** + * Convert an event to a payload in string + * + * @param event TelemetryEvent + * @return the string payload + */ public String exportQueueToString(TelemetryEvent event) { JSONArray logs = new JSONArray(); logs.add(event); @@ -509,7 +526,13 @@ private void uploadPayload() { } } - /** log OCSP exception to telemetry */ + /** + * log OCSP exception to telemetry + * + * @param eventType event type + * @param telemetryData JSON telemetry data + * @param ex CertificateException + */ public void logOCSPExceptionTelemetryEvent( String eventType, JSONObject telemetryData, CertificateException ex) { if (enabled) { @@ -533,7 +556,24 @@ public void logOCSPExceptionTelemetryEvent( } } - /** log error http response to telemetry */ + /** + * log error http response to telemetry + * + * @param eventName the event name + * @param request the HttpRequestBase + * @param injectSocketTimeout the socket timeout + * @param canceling cancelling + * @param withoutCookies without cookies + * @param includeRetryParameters include retry parameters + * @param includeRequestGuid include rest GUID + * @param response the CloseableHttpResponse + * @param savedEx the saved exception + * @param breakRetryReason the break retry reason + * @param retryTimeout the retry timeout + * @param retryCount retry count + * @param sqlState the SQL state + * @param errorCode the error code + */ public void logHttpRequestTelemetryEvent( String eventName, HttpRequestBase request, @@ -593,7 +633,12 @@ public void logHttpRequestTelemetryEvent( } } - /** log execution times from various processing slices */ + /** + * log execution times from various processing slices + * + * @param telemetryData JSON telemetry data + * @param eventName the event name + */ public void logExecutionTimeTelemetryEvent(JSONObject telemetryData, String eventName) { if (htapEnabled) { TelemetryEvent.LogBuilder logBuilder = new TelemetryEvent.LogBuilder(); diff --git a/src/main/java/net/snowflake/client/log/ArgSupplier.java b/src/main/java/net/snowflake/client/log/ArgSupplier.java index f7fef53a6..adead308d 100644 --- a/src/main/java/net/snowflake/client/log/ArgSupplier.java +++ b/src/main/java/net/snowflake/client/log/ArgSupplier.java @@ -11,5 +11,10 @@ */ @FunctionalInterface public interface ArgSupplier { + /** + * Get value + * + * @return Object value. + */ Object get(); } diff --git a/src/main/java/net/snowflake/client/log/JDK14Logger.java b/src/main/java/net/snowflake/client/log/JDK14Logger.java index d70009e16..e9ae25696 100644 --- a/src/main/java/net/snowflake/client/log/JDK14Logger.java +++ b/src/main/java/net/snowflake/client/log/JDK14Logger.java @@ -185,7 +185,9 @@ public static Level getLevel() { /** * This is way to enable logging in JDBC through TRACING parameter or sf client config file. * - * @param level + * @param level log level + * @param logPath log path + * @throws IOException if there is an error writing to the log */ public static synchronized void instantiateLogger(Level level, String logPath) throws IOException { @@ -212,6 +214,9 @@ public static synchronized void instantiateLogger(Level level, String logPath) * places. * *

This method will convert string in ex.1 to ex.2 + * + * @param original original string + * @return refactored string */ private String refactorString(String original) { StringBuilder sb = new StringBuilder(); diff --git a/src/main/java/net/snowflake/client/log/SFLogLevel.java b/src/main/java/net/snowflake/client/log/SFLogLevel.java index 18aeeb2a6..94e530af2 100644 --- a/src/main/java/net/snowflake/client/log/SFLogLevel.java +++ b/src/main/java/net/snowflake/client/log/SFLogLevel.java @@ -23,8 +23,8 @@ public enum SFLogLevel { * Method to parse the input loglevel string and returns corresponding loglevel. This method uses * case in-sensitive matching. * - * @param levelStr - * @return + * @param levelStr log level string + * @return SFLogLevel */ public static SFLogLevel getLogLevel(String levelStr) { for (SFLogLevel level : SFLogLevel.values()) { diff --git a/src/main/java/net/snowflake/client/util/SecretDetector.java b/src/main/java/net/snowflake/client/util/SecretDetector.java index 3ae48defa..3c0727de7 100644 --- a/src/main/java/net/snowflake/client/util/SecretDetector.java +++ b/src/main/java/net/snowflake/client/util/SecretDetector.java @@ -95,7 +95,8 @@ public class SecretDetector { /** * Check whether the name is sensitive * - * @param name + * @param name the name + * @return true if the name is sensitive. */ public static boolean isSensitive(String name) { return SENSITIVE_NAME_SET.contains(name.toLowerCase()); diff --git a/src/main/java/net/snowflake/client/util/TimeMeasurement.java b/src/main/java/net/snowflake/client/util/TimeMeasurement.java index 390294236..797f454c1 100644 --- a/src/main/java/net/snowflake/client/util/TimeMeasurement.java +++ b/src/main/java/net/snowflake/client/util/TimeMeasurement.java @@ -12,7 +12,11 @@ public class TimeMeasurement { private long start; private long end; - /** Get the start time as epoch time in microseconds. */ + /** + * Get the start time as epoch time in microseconds. + * + * @return the start time as epoch time in microseconds. + */ public long getStart() { return start; } @@ -22,7 +26,11 @@ public void setStart() { this.start = SnowflakeUtil.getEpochTimeInMicroSeconds(); } - /** Get the stop time as epoch time in microseconds. */ + /** + * Get the stop time as epoch time in microseconds. + * + * @return the stop time as epoch time in microseconds. + */ public long getEnd() { return end; } From d2dc3c024f31a6f0653851e10dff7c8528b7e5b4 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Wed, 13 Nov 2024 07:02:28 +0100 Subject: [PATCH 41/53] SNOW-1734031: Bump google dependencies (#1918) --- FIPS/pom.xml | 47 ++++++++++--------------------------------- parent-pom.xml | 40 ++++++++++++++++++------------------ pom.xml | 49 ++++++++++----------------------------------- thin_public_pom.xml | 25 +++++++++-------------- 4 files changed, 51 insertions(+), 110 deletions(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index ba1929bd2..a572eb653 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -398,8 +398,8 @@ ${shadeBase}.google - google.geo - ${shadeBase}.google.geo + google + ${shadeBase}.google org.joda @@ -445,18 +445,6 @@ com.carrotsearch ${shadeBase}.com.carrotsearch - - google.type - ${shadeBase}.google.type - - - google.rpc - ${shadeBase}.google.rpc - - - google.iam - ${shadeBase}.google.iam - io.opencensus ${shadeBase}.opencensus @@ -465,33 +453,13 @@ org.threeten ${shadeBase}.threeten - - google.protobuf - ${shadeBase}.google.protobuf - - - google.api - ${shadeBase}.google.api - - - google.storage - ${shadeBase}.google.storage - io.grpc ${shadeBase}.grpc - google.longrunning - ${shadeBase}.google.longrunning - - - google.cloud - ${shadeBase}.google.cloud - - - google.logging - ${shadeBase}.google.logging + io.opentelemetry + ${shadeBase}.io.opentelemetry org.checkerframework @@ -594,17 +562,24 @@ + + + + + + + diff --git a/parent-pom.xml b/parent-pom.xml index 7f165f376..17b6103f1 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -31,30 +31,31 @@ 1.0.7 1.14.17 1.1 - 3.33.0 + 3.48.2 1.2 - 1.17.0 + 1.17.1 1.4 2.17.0 1.2 1.5.4 0.9.5.4 - 2.22.0 - 1.19.0 - 2.21.0 - 2.22.6 - 2.10.1 - 2.18.0 + 2.48.0 + 1.29.0 + 2.47.0 + 2.44.1 + 2.11.0 + 2.35.1 24.3.25 - 2.31.0 - 32.1.1-jre - 1.43.3 + 2.57.0 + 33.3.1-jre + 1.45.0 + 3.0.0 3.0.2 - 3.25.5 - 1.60.0 + 4.28.2 + 1.68.1 2.2 2.4.3 - 2.17.2 + 2.18.1 true 3.1.0 5.13.0 @@ -77,7 +78,6 @@ 2.0.13 5.1.4 net.snowflake.client.category.AllTestCategory - 1.6.9 2.4.1 1.9 3.6.3 @@ -200,6 +200,11 @@ guava ${google.guava.version} + + com.google.j2objc + j2objc-annotations + ${google.j2objc-annotations.version} + com.microsoft.azure azure-storage @@ -474,11 +479,6 @@ ${bouncycastle.bcpkixfips.version} provided - - org.threeten - threetenbp - ${threeten.version} - org.tukaani xz diff --git a/pom.xml b/pom.xml index 69ff5b183..e80b0c75f 100644 --- a/pom.xml +++ b/pom.xml @@ -824,12 +824,8 @@ ${shadeBase}.google - google.geo - ${shadeBase}.google.geo - - - google.storage - ${shadeBase}.google.storage + google + ${shadeBase}.google org.joda @@ -876,20 +872,12 @@ ${shadeBase}.io.netty - com.carrotsearch - ${shadeBase}.com.carrotsearch + io.opentelemetry + ${shadeBase}.io.opentelemetry - google.type - ${shadeBase}.google.type - - - google.rpc - ${shadeBase}.google.rpc - - - google.iam - ${shadeBase}.google.iam + com.carrotsearch + ${shadeBase}.com.carrotsearch io.opencensus @@ -899,30 +887,10 @@ org.threeten ${shadeBase}.threeten - - google.protobuf - ${shadeBase}.google.protobuf - - - google.api - ${shadeBase}.google.api - io.grpc ${shadeBase}.grpc - - google.longrunning - ${shadeBase}.google.longrunning - - - google.cloud - ${shadeBase}.google.cloud - - - google.logging - ${shadeBase}.google.logging - org.checkerframework ${shadeBase}.org.checkerframework @@ -1031,6 +999,7 @@ + @@ -1040,12 +1009,16 @@ + + + + diff --git a/thin_public_pom.xml b/thin_public_pom.xml index 460ceaec2..1d3d26fdd 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -41,17 +41,17 @@ 1.17.0 2.17.0 1.2 - 2.21.0 - 2.22.6 + 1.29.0 + 2.47.0 + 2.44.1 24.3.25 - 1.19.0 - 2.31.0 - 32.1.1-jre - 1.43.3 + 2.57.0 + 33.3.1-jre + 1.45.0 3.0.2 - 3.25.5 - 1.60.0 - 2.17.2 + 4.28.2 + 1.68.1 + 2.18.1 3.1.0 5.13.0 2.8.1 @@ -63,7 +63,6 @@ UTF-8 UTF-8 2.0.13 - 1.6.9 1.5.6-5 @@ -156,12 +155,6 @@ gax ${google.gax.version} - - - org.threeten - threetenbp - ${threeten.version} - com.google.auth google-auth-library-oauth2-http From 2314d35d73b7cccc6512d47a05066236f3268e51 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:57:54 +0100 Subject: [PATCH 42/53] SNOW-1799642: Add array bind supported log for prepared statements (#1960) --- .../client/jdbc/SnowflakePreparedStatementV1.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java index 000d4634d..cb293690d 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java @@ -58,14 +58,19 @@ class SnowflakePreparedStatementV1 extends SnowflakeStatementV1 implements PreparedStatement, SnowflakePreparedStatement { private static final SFLogger logger = SFLoggerFactory.getLogger(SnowflakePreparedStatementV1.class); + /** Error code returned when describing a statement that is binding table name */ private static final Integer ERROR_CODE_TABLE_BIND_VARIABLE_NOT_SET = 2128; + /** Error code when preparing statement with binding object names */ private static final Integer ERROR_CODE_OBJECT_BIND_NOT_SET = 2129; + /** Error code returned when describing a ddl command */ private static final Integer ERROR_CODE_STATEMENT_CANNOT_BE_PREPARED = 7; + /** snow-44393 Workaround for compiler cannot prepare to_timestamp(?, 3) */ private static final Integer ERROR_CODE_FORMAT_ARGUMENT_NOT_STRING = 1026; + /** A hash set that contains the error code that will not lead to exception in describe mode */ private static final Set errorCodesIgnoredInDescribeMode = new HashSet<>( @@ -88,10 +93,12 @@ class SnowflakePreparedStatementV1 extends SnowflakeStatementV1 *

Currently, bind name is just value index */ private Map parameterBindings = new HashMap<>(); + /** map of bind values for batch query executions */ private Map batchParameterBindings = new HashMap<>(); private Map wasPrevValueNull = new HashMap<>(); + /** Counter for batch size if we are executing a statement with array bind supported */ private int batchSize = 0; @@ -133,6 +140,12 @@ private void describeSqlIfNotTried() throws SQLException { if (!alreadyDescribed) { try { this.preparedStatementMetaData = sfBaseStatement.describe(sql); + if (preparedStatementMetaData != null + && !preparedStatementMetaData.isArrayBindSupported()) { + logger.debug( + "Array bind is not supported - each batch entry will be executed as a single request for query: {}", + sql); + } } catch (SFException e) { throw new SnowflakeSQLLoggedException(connection.getSFBaseSession(), e); } catch (SnowflakeSQLException e) { From be85cf7092b22a9b77cf1e41c7afacbc33ac59c0 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:06:26 +0100 Subject: [PATCH 43/53] SNOW-1803984: Fix shading of google properties (#1965) --- FIPS/pom.xml | 61 ++++++++++++++++++++++++++++++++++++++++++++++------ pom.xml | 61 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 110 insertions(+), 12 deletions(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index a572eb653..13e7db8f4 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -397,9 +397,58 @@ com.google ${shadeBase}.google + - google - ${shadeBase}.google + google.api + ${shadeBase}.google.api + + + google.apps + ${shadeBase}.google.apps + + + google.cloud + ${shadeBase}.google.cloud + + + google.geo + ${shadeBase}.google.geo + + + google.iam + ${shadeBase}.google.iam + + + google.logging + ${shadeBase}.google.logging + + + google.longrunning + ${shadeBase}.google.longrunning + + + google.monitoring + ${shadeBase}.google.monitoring + + + google.protobuf + ${shadeBase}.google.protobuf + + + google.rpc + ${shadeBase}.google.rpc + + + google.shopping + ${shadeBase}.google.shopping + + + google.storage + ${shadeBase}.google.storage + + + google.type + ${shadeBase}.google.type org.joda @@ -449,6 +498,10 @@ io.opencensus ${shadeBase}.opencensus + + io.opentelemetry + ${shadeBase}.opentelemetry + org.threeten ${shadeBase}.threeten @@ -457,10 +510,6 @@ io.grpc ${shadeBase}.grpc - - io.opentelemetry - ${shadeBase}.io.opentelemetry - org.checkerframework ${shadeBase}.org.checkerframework diff --git a/pom.xml b/pom.xml index e80b0c75f..96f2a65b6 100644 --- a/pom.xml +++ b/pom.xml @@ -823,9 +823,58 @@ com.google ${shadeBase}.google + - google - ${shadeBase}.google + google.api + ${shadeBase}.google.api + + + google.apps + ${shadeBase}.google.apps + + + google.cloud + ${shadeBase}.google.cloud + + + google.geo + ${shadeBase}.google.geo + + + google.iam + ${shadeBase}.google.iam + + + google.logging + ${shadeBase}.google.logging + + + google.longrunning + ${shadeBase}.google.longrunning + + + google.monitoring + ${shadeBase}.google.monitoring + + + google.protobuf + ${shadeBase}.google.protobuf + + + google.rpc + ${shadeBase}.google.rpc + + + google.shopping + ${shadeBase}.google.shopping + + + google.storage + ${shadeBase}.google.storage + + + google.type + ${shadeBase}.google.type org.joda @@ -871,10 +920,6 @@ io.netty ${shadeBase}.io.netty - - io.opentelemetry - ${shadeBase}.io.opentelemetry - com.carrotsearch ${shadeBase}.com.carrotsearch @@ -883,6 +928,10 @@ io.opencensus ${shadeBase}.opencensus + + io.opentelemetry + ${shadeBase}.opentelemetry + org.threeten ${shadeBase}.threeten From 4364399bd454b290851927bc927e1cbd9d36823c Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:43:51 +0100 Subject: [PATCH 44/53] SNOW-1800995: Merge io.netty.versions.properties during shade (#1967) --- FIPS/pom.xml | 4 +++- pom.xml | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 13e7db8f4..0b874551d 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -589,7 +589,9 @@ - + + + META-INF/io.netty.versions.properties diff --git a/pom.xml b/pom.xml index 96f2a65b6..2a1cf4ea6 100644 --- a/pom.xml +++ b/pom.xml @@ -1023,6 +1023,9 @@ + + META-INF/io.netty.versions.properties + From e57b1ccf50163fda0dd01d24578900cf37f7878e Mon Sep 17 00:00:00 2001 From: John Yun <140559986+sfc-gh-ext-simba-jy@users.noreply.github.com> Date: Mon, 18 Nov 2024 22:18:35 -0800 Subject: [PATCH 45/53] SNOW-1787996: Uncontrolled logging in snowflake-jdbc (#1966) --- dependencies/Readme.md | 2 +- dependencies/arrow-format-17.0.0.jar | Bin 118095 -> 118063 bytes dependencies/arrow-memory-core-17.0.0.jar | Bin 122805 -> 122795 bytes ...arrow-memory-netty-buffer-patch-17.0.0.jar | Bin 35441 -> 35409 bytes dependencies/arrow-memory-unsafe-17.0.0.jar | Bin 10746 -> 10714 bytes dependencies/arrow-vector-17.0.0.jar | Bin 2063599 -> 2063567 bytes 6 files changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/Readme.md b/dependencies/Readme.md index 5abaea2ae..28afe4031 100644 --- a/dependencies/Readme.md +++ b/dependencies/Readme.md @@ -1,2 +1,2 @@ -Arrow dependencies are built from internal branch `upgradeTo17.0.0-v2`. This build was applied the AIX fix and the customer logger instead of slf4j logger. +Arrow dependencies are built from internal branch `upgradeTo17.0.0-v3`. This build was applied the AIX fix and the customer logger instead of slf4j logger. diff --git a/dependencies/arrow-format-17.0.0.jar b/dependencies/arrow-format-17.0.0.jar index b4a34e86febd233cb6b3595c7e1bd9ddbea83687..103c9c00de6d6deee775ea57bab4a58c1ab3b82d 100644 GIT binary patch delta 2184 zcmY*bc~n$o6!*Tz;$aYG6d`dGLZQHb5g{x`Ku}}L0*-)$x#cm12(w59vZJC0Ad z3aD9b!!nF846@oNiZX=a(M;)3+9p<{m1!_y_q~S`&L12}6DM1n94J zTyfhq_Roh!DIb^iTSeN1F7IvbR`z{WkaG{`9yb}> z7RC&8Z8It7s&9GTEB5P%Z(oyB`%y{B?l(8}&fc6-^0VKk85V|&z3Y-rW|w<>oF7>5 zSm(81^5jE4>(=FlFWdX|b{B)aN9M+^p{{{}=?p(3c#(d-vz^W6OVVu(|QYddU+_Q~oc}`@8dJ{(8G~Uc!vNa=XD2yf$?0 zL2TAdY1)xR-bt4y9=$RD!Ry9viUZ2B>l@W;T6@a%tEy6iGqU1-O;CCj?MOF;W$#)O zzF227R8SqO+L2H@KRm)NWbn7L@GW!0d~3sQp9zu64KmxQMPd0BZ4G~Wcb4|{$NMh6 zqS+VGvu#y(6c!&$zH{WQ`FW9StR%xSW@%-1j$0w*eDba_y<2`jvF%cyc1g7)^~IW! zn~5*DENJ0^=Ty`cPA}^S-1{~L`)HzfPMN;6q0hzoulE+?zk`;!j(SJm6VEIhPh9 z#WXbqR8&zwStw894^>nx=`{iSYe<@V1Yut>uu?;T+9)F2IJ;;oyr-eHSj6BQEx2pM;EY1~u zFjCB_4|p$Lt^oxEAk;!Kqs}I3D(2PFw(rj+Jc--6bqs!2OHKT?4%_1}Tt|%_ZRc%N z=^&KZRVes=E|iH!v|cr`QKX8za;&TaTTY`IUg(E)w0+-mPd!SeexHDs>cJ8vdTJP4 z`jiM@5|45*Rgtc3Hsi32yijoLSb|?3=|F$V8)82F~IpL^*(T%>VO0$-x{j z1~UwxU zi1cS?A&mXQPcVwxK*@S2MaPzNAj40UKo3wGc(Hy#W_m1^27$fU;!Yd2xE3FJ9IZ`k!USuN9Ll9e+C>pklXy^DB4C|8VtIPm4Y(cWE7 tBh7N;e$g1+Nu{oripD#gR7!IQO`R|Uzv={Un!dqKSYV|)LVr3&{RbfvM|A)I delta 2218 zcmY*bYgAKL7G~dzs30!|!59$47orjc6byz()q*%6&?2HOVrn(MsxFG6gN#M1gA6vk z(ruyEPHW4<00F`q50w7KfKPC#B(cj|K+*X1!J0m2v!qqE4-Ny|Jz&F>ZQK z!L_io)azjF%X(tFY(&4MS=nCQoAm$vvaWW>)n)>P`vBdlhiCOZA_4ujiA@ z&$|44BXMGd+ov8U6|bJwsrEKLNvm`cA~StwqLaZaqt~&z*AaeEk2+xN&W9MzY%RqaEFAWb;)wzXu)3-`oY;mK0UpOjz}<_m!d)~xCnEl8jO-L&vNm#bvi^-4=D)lzoi zX3@%^78KlrEh3IBBU_a_gt8r8SY1Xr8M}n&G${_3!D4RcBf@Ti^KMpL_;~MVGspHC zqo^$h8N7>q<*8B zG1iu4&yFZwJ-X30HASVERS6S>Z1u>4zEertXLjmMB#`^*w1|VNU<^K}q=t!kZv;PX z|M$YeVpq(mBK^PlB7Rs!CjE*D%P_K<3^kP!_7HkYEtrW^-DndhbYiqfXNg0`?2IaL z2F}*NIE<*FvNpAE1b^X7}A@Lk)jKZ9S~z zZ0`t3ebaDG12z1)hp-2xHBiINeS|&nZUZ%x_hXVCq&T~gZk7R)8X*c!Vp$_3aFR#H z&~)_H(|YL(BZ=pJ_tN<5g17Z_bK{1Lk1OmPVs3@YukCP0C*7*<*Wx~88o(C{(O`gg z`7Cn~+@mG<*#)q|5_1?i!gUuw!h4K0#pHaxJtKO1kg!egasWH)|6hP*4yFWajB5fJ zKh>Ej-!;K9_!j$`AcmhelWA+3VHGc(Z6XseXf`;q!UN6JT<`hY*f{jLNHq`0nEZ!} z@D^|D!$|K%2;^4=GP1E1>~U8h(EFza=JGKLlROa*D!`WY=xCuH&w@wATtjGFrnJ(M z>~NDD!7p5GA`aNyN){sGnBv(63jSt1BM;+g>x}O(%-KL+zi$FdK7Rv?tx=Jgo;Fy* zdnb-k{M)HmfXbvyX3oyHle0_PncTab42xyY?V+;9cOzf5hs8eY0CNoNptAC0lj6(o zOEnR5?C$_ye$T;CO4dPO?=ReNi5hW-M#WqX(GZB;`ZIoWwu2cg%jO8Mp~mCK$c|Sz z4OQ+jbnB#1m0zaWch6+x)MdI9QEEoEsKEwR6|`WCF!B(blVFU+ac6Rjzta#qX_~iv o$;dz_l^V=rWO)~rx_TD7yTAvFyI?*n#XDUPX>sEm{Vg&3A3Lu@$p8QV diff --git a/dependencies/arrow-memory-core-17.0.0.jar b/dependencies/arrow-memory-core-17.0.0.jar index 12d9c9116dc1242937aaa40993259eb331662c47..916e38238080a99b7c33949baee5cce45043fa4e 100644 GIT binary patch delta 4857 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 Lf}$qqg$w&X@u9JQ diff --git a/dependencies/arrow-memory-netty-buffer-patch-17.0.0.jar b/dependencies/arrow-memory-netty-buffer-patch-17.0.0.jar index 72d3742479bcabb1b0d0002a4feed11dec4771ee..63f032a2b99ef3275eca1c100c651cae6465f4bf 100644 GIT binary patch delta 646 zcmex3h3VoHrVV#R>t$zbdb*U6f#EhY1A_j#B+}_^&+`Fec=}j`-cIDJ1rCy;=-=}7m&wj_|#pjfC zZ_h=WmuK6nKM1HcslD=fr@PLswNU4__};35$_@Yi?mnm#8u;s6$Deu|mz57SKTI<^ z6?QzUadYXcSrYWm?W4Xe#~4)|Fmg*^T#LE-=@D1Oxkp?d*@5ZmoXj_CM=R! zmh@PJ>*nGL?*Hp{w>R1+*}iLK*1p@TwxzGlu`jC)ejRS3HMOhYk8^!*mPhOsu7B<8gC=%`Z+7$W z4J+ga}7GqzuXYNo;zpLtXmQ4@_u_K+dHn_ z+G9B9l=+mn>Gpc-e7cnl6L!9oXaAfVJN@Tx*3HMnU30k*F*;eaHHdN9{oq z@`Uyprk?>o0gK6Q9R^GjV}MM}$qRwZbxD(7B^iK?74DQ`axI_SSZ+Pp2WU$bklRr+ bd2O@OWZf>6$#*(+nRpwZTsyXt4It|QcoiqI delta 697 zcmcaOh3VrIrVV#R>t7y`Gdar0z#z@Sz#ziFz>ru}lwYo!o>`(-P?TSgT2zvmTD&$m zI{UVPNS*!{dx>urYdrQH<>ZJm(~wl=-m*seiR-4m+}uqfC7HA9?XAnUZ#m1>Xe*Ie zJALu{7ccJeW^6dq-0{P1*F?L23A@(Chbi4#alxau@}fuoj?`UQne{hJr*VF0J0s{; zai}STY3s4dUtg>K{;b=2%)-0RP~UpN4wse%Eb5shq7!p=%j|1$nSFbAa$Z4#V2O{G zm(iT(-L8>)%1cGfb9wyj$~HaL`L}VuR0m&Xa^muDE6g*P-KW01HzV^}`2UnB*;@Wz z616XQmt6c};j^@d`T4~3vkBMgd85@b{O=!Pzn@v1vgtu|^6?#-jum!C&Mw>Gxi2;? zbIZ?)?@q>ga_6RaItK4K;k6>jW7DLmm4O?Xf(}iVc`mbRjowF@pui8|R!1zhfB2wN z-^*1nb*jv~j}acPzt<-pUC00C|G~EhEUz)FY7Y8R_V=ls-l-D5t`}8bb(5~mI=-Ub zY+JJ3s;8GdpGG{?c^NtPk3Vzyo3f-wd~+AxW0Uwlb>)m%^F)($4IDpmwm55l%|GFk zZljQ#`rjaML)W%@@9&gZ&i-q8?o8(A2<>meEc0b=U5OXktk9<1Z}<1huFRsnA8WUY zPxk9FQJpTnpCQ1TnFScf92^X%H)ez{nG~;Ev3aMsQ7#uE5lsHt62y3HvPWwOQ?vZ! zdU@r^?HvM>kEsf91Jec=ENQ$k`AusCmYfV1bX2`VB6U>xr4`lp5 zxuCsdH43Mcgxe>@bmo)iQk^$J*&mB@sspXRs%dIEt0Bwotlww*@ cGr6}}X)OO21*V>V&1&bJn7Acd+-n?f+p%r?k%o9g>fv~ z7rlCLd9eQdyTKw=QTon^{5(Xd;VJNzGRjuRYJszLnwpSMeBM2NYntXVj&=mWc9e5k zv45&2cZ4^G{j@4h9v+`Ab8~`7VU*U1AHfeMdwS0P&iZ5rgDLcQk`ZVZ#y2c4%Qatv z@#K5*AY5S2O9V4*Qyy z)&AA>QhnMGZlqZ)TF@J%*+%tE%h;ARoPW2vscOF4sldmT}0*x1wVJIP!5GYRq>lu?_C~5*K9Ft)vIFp_n29paZDgvS(lb|U& UlW{360U48@DOCo+BLDyZ0MB3c1^@s6 delta 562 zcmV-20?qx}Q~Fb|2NeqG#2z%o0RR9S1d|aJ8h@=*TWi}e6n-!CKN$8ntVq7a2?$y? zC>bLQw5%KCMY4`Z*v#(Dbndj0u+wZ=G0jxjGgptwYY zpnn7>X>=8lY`Yx3ghZR$yJK!}U=egoDoQe(=Zi&IymxgKH*E}0K6bL>EdH^-8i)>Q zV_~QEs5fW>N0sT`m}%Bm|72PneGmE=edrCO(eyY>rHljTk!Q_d)(u-GXiwk52j6LX zWU}B}W5>8DL3lpI&8E0XzFlT%wE1}VLVq(Z9?q0WK~ucQN~lsuvXYf~QLzG2!jl~5 z9IC7?@f=c7@K-v-IJfZdEPss(aFvxDp7B^n>-T?S#jX$R|H19RIo1HGgHrT%{pCI` z%5+YO=z8@nV%D0+s5Z7^KC0!@N#$7KEa_RD{7(aS>~><}4xORi1{nX9shFCc6-Z(& zFhTJNghMO!Z^%MtJRoCb|1eRoiniYG+;%ye{W;FeY4cbs_7eo39op1i60;zNBac4) z^tx$sz43ju7n4qkG$of8zW}q)7Ge+z>BJs1#Q^{S8w8U)CsG0MlaD7p0<9O5K`0@U z@F!0K3mTI_C~5*q9FsvPIFo)H29xV3DgweElYl8YlTj%x0XdU@DOCpHBLDyZ07Vo8 AO8@`> diff --git a/dependencies/arrow-vector-17.0.0.jar b/dependencies/arrow-vector-17.0.0.jar index 29055d60315db4241acc51a1368993fd4860257f..73061da73f30d1a512c3dc365c0b06537d709bc4 100644 GIT binary patch delta 14386 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 Mui6$U;8%|S2SsO7qW}N^ delta 14403 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 From 6dd6ea0e3145c45ce47f5050f027a6f38d038ac4 Mon Sep 17 00:00:00 2001 From: sfc-gh-snowflakedb-snyk-sa <130097897+sfc-gh-snowflakedb-snyk-sa@users.noreply.github.com> Date: Mon, 18 Nov 2024 22:18:56 -0800 Subject: [PATCH 46/53] SNOW-1813471: CVE-2024-47535 [Snyk] Fix for 1 vulnerabilities (#1962) Co-authored-by: snyk-bot Co-authored-by: Dominik Przybysz --- TestOnly/pom.xml | 4 ++-- parent-pom.xml | 2 +- thin_public_pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TestOnly/pom.xml b/TestOnly/pom.xml index 0b8143b5f..e03f87ef1 100644 --- a/TestOnly/pom.xml +++ b/TestOnly/pom.xml @@ -27,12 +27,12 @@ io.netty netty-common - 4.1.111.Final + 4.1.115.Final io.netty netty-buffer - 4.1.111.Final + 4.1.115.Final org.apache.maven.plugins diff --git a/parent-pom.xml b/parent-pom.xml index 17b6103f1..d69c49cbf 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -66,7 +66,7 @@ 1.3.6 2.2.0 4.11.0 - 4.1.111.Final + 4.1.115.Final 9.37.3 0.31.1 1.0-alpha-9-stable-1 diff --git a/thin_public_pom.xml b/thin_public_pom.xml index 1d3d26fdd..09c6bf079 100644 --- a/thin_public_pom.xml +++ b/thin_public_pom.xml @@ -58,7 +58,7 @@ 2.4.9 1.15.3 2.2.0 - 4.1.111.Final + 4.1.115.Final 9.37.3 UTF-8 UTF-8 From 1286a3eba65442573afa55037093c791f57c317e Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Tue, 19 Nov 2024 07:28:35 +0100 Subject: [PATCH 47/53] SNOW-1800995: Merge io.netty.versions.properties during shading thin (#1969) --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 2a1cf4ea6..45e57505b 100644 --- a/pom.xml +++ b/pom.xml @@ -724,6 +724,9 @@ + + META-INF/io.netty.versions.properties + From 938d8468653a1ff8641417c6700cf727bbc435b5 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:02:14 +0100 Subject: [PATCH 48/53] SNOW-1789749: Support regional GCS endpoints (#1972) --- .../jdbc/SnowflakeFileTransferAgent.java | 24 +- .../cloud/storage/SnowflakeGCSClient.java | 9 +- .../client/jdbc/cloud/storage/StageInfo.java | 46 ++- .../cloud/storage/StorageClientFactory.java | 5 +- .../client/jdbc/FileUploaderPrep.java | 273 +++--------------- .../jdbc/FileUploaderSessionlessTest.java | 56 +++- .../StageInfoGcsCustomEndpointTest.java | 57 ++++ .../FileUploaderPrep/exampleAzure.json | 51 ++++ .../FileUploaderPrep/exampleGCS.json | 47 +++ .../exampleGCSWithEndpoint.json | 47 +++ .../exampleGCSWithUseRegionalUrl.json | 49 ++++ .../resources/FileUploaderPrep/exampleS3.json | 60 ++++ .../exampleS3WithStageEndpoint.json | 59 ++++ 13 files changed, 525 insertions(+), 258 deletions(-) create mode 100644 src/test/java/net/snowflake/client/jdbc/cloud/storage/StageInfoGcsCustomEndpointTest.java create mode 100644 src/test/resources/FileUploaderPrep/exampleAzure.json create mode 100644 src/test/resources/FileUploaderPrep/exampleGCS.json create mode 100644 src/test/resources/FileUploaderPrep/exampleGCSWithEndpoint.json create mode 100644 src/test/resources/FileUploaderPrep/exampleGCSWithUseRegionalUrl.json create mode 100644 src/test/resources/FileUploaderPrep/exampleS3.json create mode 100644 src/test/resources/FileUploaderPrep/exampleS3WithStageEndpoint.json diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java index 2b660eb08..4213b33b0 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeFileTransferAgent.java @@ -1111,8 +1111,16 @@ static StageInfo getStageInfo(JsonNode jsonNode, SFSession session) throws Snowf // specifically // for FIPS or VPCE S3 endpoint. SNOW-652696 String endPoint = null; - if ("AZURE".equalsIgnoreCase(stageLocationType) || "S3".equalsIgnoreCase(stageLocationType)) { + if ("AZURE".equalsIgnoreCase(stageLocationType) + || "S3".equalsIgnoreCase(stageLocationType) + || "GCS".equalsIgnoreCase(stageLocationType)) { endPoint = jsonNode.path("data").path("stageInfo").findValue("endPoint").asText(); + if ("GCS".equalsIgnoreCase(stageLocationType) + && endPoint != null + && (endPoint.trim().isEmpty() || "null".equals(endPoint))) { + // setting to null to preserve previous behaviour for GCS + endPoint = null; + } } String stgAcct = null; @@ -1179,6 +1187,8 @@ static StageInfo getStageInfo(JsonNode jsonNode, SFSession session) throws Snowf } } + setupUseRegionalUrl(jsonNode, stageInfo); + if (stageInfo.getStageType() == StageInfo.StageType.S3) { if (session == null) { // This node's value is set if PUT is used without Session. (For Snowpipe Streaming, we rely @@ -1200,6 +1210,18 @@ static StageInfo getStageInfo(JsonNode jsonNode, SFSession session) throws Snowf return stageInfo; } + private static void setupUseRegionalUrl(JsonNode jsonNode, StageInfo stageInfo) { + if (stageInfo.getStageType() != StageInfo.StageType.GCS + && stageInfo.getStageType() != StageInfo.StageType.S3) { + return; + } + JsonNode useRegionalURLNode = jsonNode.path("data").path("stageInfo").path("useRegionalUrl"); + if (!useRegionalURLNode.isMissingNode()) { + boolean useRegionalURL = useRegionalURLNode.asBoolean(false); + stageInfo.setUseRegionalUrl(useRegionalURL); + } + } + /** * A helper method to verify if the local file path from GS matches what's parsed locally. This is * for security purpose as documented in SNOW-15153. diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java index 188ba40d4..d6bf6ba84 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeGCSClient.java @@ -18,9 +18,11 @@ import com.google.api.gax.rpc.FixedHeaderProvider; import com.google.auth.oauth2.AccessToken; import com.google.auth.oauth2.GoogleCredentials; +import com.google.cloud.NoCredentials; import com.google.cloud.storage.Blob; import com.google.cloud.storage.BlobId; import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.HttpStorageOptions; import com.google.cloud.storage.Storage; import com.google.cloud.storage.Storage.BlobListOption; import com.google.cloud.storage.StorageException; @@ -1312,6 +1314,8 @@ private void setupGCSClient( if (accessToken != null) { // We are authenticated with an oauth access token. StorageOptions.Builder builder = StorageOptions.newBuilder(); + stage.gcsCustomEndpoint().ifPresent(builder::setHost); + if (areDisabledGcsDefaultCredentials(session)) { logger.debug( "Adding explicit credentials to avoid default credential lookup by the GCS client"); @@ -1329,7 +1333,10 @@ private void setupGCSClient( .getService(); } else { // Use anonymous authentication. - this.gcsClient = StorageOptions.getUnauthenticatedInstance().getService(); + HttpStorageOptions.Builder builder = + HttpStorageOptions.newBuilder().setCredentials(NoCredentials.getInstance()); + stage.gcsCustomEndpoint().ifPresent(builder::setHost); + this.gcsClient = builder.build().getService(); } if (encMat != null) { diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/StageInfo.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/StageInfo.java index 7a8bf4d36..3a14b8fa0 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/StageInfo.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/StageInfo.java @@ -2,10 +2,17 @@ import java.io.Serializable; import java.util.Map; +import java.util.Optional; import java.util.Properties; +import net.snowflake.client.core.SnowflakeJdbcInternalApi; -/** Encapsulates all the required stage properties used by GET/PUT for Azure and S3 stages */ +/** Encapsulates all the required stage properties used by GET/PUT for Azure, GCS and S3 stages */ public class StageInfo implements Serializable { + + // me-central2 GCS region always use regional urls + // TODO SNOW-1818804: the value is hardcoded now, but it should be server driven + private static final String GCS_REGION_ME_CENTRAL_2 = "me-central2"; + public enum StageType { S3, AZURE, @@ -17,12 +24,18 @@ public enum StageType { private StageType stageType; // The stage type private String location; // The container or bucket private Map credentials; // the credentials required for the stage - private String region; // AWS/S3/GCS region (S3/GCS only) - private String endPoint; // The Azure Storage endpoint (Azure only) + private String region; // S3/GCS region + // An endpoint (Azure, AWS FIPS and GCS custom endpoint override) + private String endPoint; private String storageAccount; // The Azure Storage account (Azure only) private String presignedUrl; // GCS gives us back a presigned URL instead of a cred private boolean isClientSideEncrypted; // whether to encrypt/decrypt files on the stage - private boolean useS3RegionalUrl; // whether to use s3 regional URL (AWS Only) + // whether to use s3 regional URL (AWS Only) + // TODO SNOW-1818804: this field will be deprecated when the server returns {@link + // #useRegionalUrl} + private boolean useS3RegionalUrl; + // whether to use regional URL (AWS and GCS only) + private boolean useRegionalUrl; private Properties proxyProperties; /* @@ -166,6 +179,16 @@ public boolean getUseS3RegionalUrl() { return useS3RegionalUrl; } + @SnowflakeJdbcInternalApi + public void setUseRegionalUrl(boolean useRegionalUrl) { + this.useRegionalUrl = useRegionalUrl; + } + + @SnowflakeJdbcInternalApi + public boolean getUseRegionalUrl() { + return useRegionalUrl; + } + private static boolean isSpecified(String arg) { return !(arg == null || arg.equalsIgnoreCase("")); } @@ -173,9 +196,22 @@ private static boolean isSpecified(String arg) { public void setProxyProperties(Properties proxyProperties) { this.proxyProperties = proxyProperties; } - ; public Properties getProxyProperties() { return proxyProperties; } + + @SnowflakeJdbcInternalApi + public Optional gcsCustomEndpoint() { + if (stageType != StageType.GCS) { + return Optional.empty(); + } + if (endPoint != null && !endPoint.trim().isEmpty() && !"null".equals(endPoint)) { + return Optional.of(endPoint); + } + if (GCS_REGION_ME_CENTRAL_2.equalsIgnoreCase(region) || useRegionalUrl) { + return Optional.of(String.format("storage.%s.rep.googleapis.com", region.toLowerCase())); + } + return Optional.empty(); + } } diff --git a/src/main/java/net/snowflake/client/jdbc/cloud/storage/StorageClientFactory.java b/src/main/java/net/snowflake/client/jdbc/cloud/storage/StorageClientFactory.java index a321b6ebd..69d56e195 100644 --- a/src/main/java/net/snowflake/client/jdbc/cloud/storage/StorageClientFactory.java +++ b/src/main/java/net/snowflake/client/jdbc/cloud/storage/StorageClientFactory.java @@ -59,8 +59,9 @@ public SnowflakeStorageClient createClient( switch (stage.getStageType()) { case S3: boolean useS3RegionalUrl = - (stage.getUseS3RegionalUrl() - || (session != null && session.getUseRegionalS3EndpointsForPresignedURL())); + stage.getUseS3RegionalUrl() + || stage.getUseRegionalUrl() + || session != null && session.getUseRegionalS3EndpointsForPresignedURL(); return createS3Client( stage.getCredentials(), parallel, diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java index d8aa8143f..d801a00ac 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java @@ -6,254 +6,49 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; import java.util.Arrays; import java.util.List; -import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.rules.TemporaryFolder; /** File uploader test prep reused by IT/connection tests and sessionless tests */ abstract class FileUploaderPrep extends BaseJDBCTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); - private ObjectMapper mapper = new ObjectMapper(); - private final String exampleS3JsonStringWithStageEndpoint = - "{\n" - + " \"data\": {\n" - + " \"uploadInfo\": {\n" - + " \"locationType\": \"S3\",\n" - + " \"location\": \"example/location\",\n" - + " \"path\": \"tables/19805757505/\",\n" - + " \"region\": \"us-west-2\",\n" - + " \"storageAccount\": null,\n" - + " \"isClientSideEncrypted\": true,\n" - + " \"creds\": {\n" - + " \"AWS_KEY_ID\": \"EXAMPLE_AWS_KEY_ID\",\n" - + " \"AWS_SECRET_KEY\": \"EXAMPLE_AWS_SECRET_KEY\",\n" - + " \"AWS_TOKEN\": \"EXAMPLE_AWS_TOKEN\",\n" - + " \"AWS_ID\": \"EXAMPLE_AWS_ID\",\n" - + " \"AWS_KEY\": \"EXAMPLE_AWS_KEY\"\n" - + " },\n" - + " \"presignedUrl\": null,\n" - + " \"endPoint\": null\n" - + " },\n" - + " \"src_locations\": [\n" - + " \"/tmp/files/orders_100.csv\"\n" - + " ],\n" - + " \"parallel\": 4,\n" - + " \"threshold\": 209715200,\n" - + " \"autoCompress\": true,\n" - + " \"overwrite\": false,\n" - + " \"sourceCompression\": \"auto_detect\",\n" - + " \"clientShowEncryptionParameter\": true,\n" - + " \"queryId\": \"EXAMPLE_QUERY_ID\",\n" - + " \"encryptionMaterial\": {\n" - + " \"queryStageMasterKey\": \"EXAMPLE_QUERY_STAGE_MASTER_KEY\",\n" - + " \"queryId\": \"EXAMPLE_QUERY_ID\",\n" - + " \"smkId\": 123\n" - + " },\n" - + " \"stageInfo\": {\n" - + " \"locationType\": \"S3\",\n" - + " \"location\": \"stage/location/foo/\",\n" - + " \"path\": \"tables/19805757505/\",\n" - + " \"region\": \"us-west-2\",\n" - + " \"storageAccount\": null,\n" - + " \"isClientSideEncrypted\": true,\n" - + " \"creds\": {\n" - + " \"AWS_KEY_ID\": \"EXAMPLE_AWS_KEY_ID\",\n" - + " \"AWS_SECRET_KEY\": \"EXAMPLE_AWS_SECRET_KEY\",\n" - + " \"AWS_TOKEN\": \"EXAMPLE_AWS_TOKEN\",\n" - + " \"AWS_ID\": \"EXAMPLE_AWS_ID\",\n" - + " \"AWS_KEY\": \"EXAMPLE_AWS_KEY\"\n" - + " },\n" - + " \"presignedUrl\": null,\n" - + " \"endPoint\": \"s3-fips.us-east-1.amazonaws.com\"\n" - + " },\n" - + " \"command\": \"UPLOAD\",\n" - + " \"kind\": null,\n" - + " \"operation\": \"Node\"\n" - + " },\n" - + " \"code\": null,\n" - + " \"message\": null,\n" - + " \"success\": true\n" - + "}"; - - private final String exampleS3JsonString = - "{\n" - + " \"data\": {\n" - + " \"uploadInfo\": {\n" - + " \"locationType\": \"S3\",\n" - + " \"location\": \"example/location\",\n" - + " \"path\": \"tables/19805757505/\",\n" - + " \"region\": \"us-west-2\",\n" - + " \"storageAccount\": null,\n" - + " \"isClientSideEncrypted\": true,\n" - + " \"creds\": {\n" - + " \"AWS_KEY_ID\": \"EXAMPLE_AWS_KEY_ID\",\n" - + " \"AWS_SECRET_KEY\": \"EXAMPLE_AWS_SECRET_KEY\",\n" - + " \"AWS_TOKEN\": \"EXAMPLE_AWS_TOKEN\",\n" - + " \"AWS_ID\": \"EXAMPLE_AWS_ID\",\n" - + " \"AWS_KEY\": \"EXAMPLE_AWS_KEY\"\n" - + " },\n" - + " \"presignedUrl\": null,\n" - + " \"endPoint\": null\n" - + " },\n" - + " \"src_locations\": [\n" - + " \"/tmp/files/orders_100.csv\"\n" - + " ],\n" - + " \"parallel\": 4,\n" - + " \"threshold\": 209715200,\n" - + " \"autoCompress\": true,\n" - + " \"overwrite\": false,\n" - + " \"sourceCompression\": \"auto_detect\",\n" - + " \"clientShowEncryptionParameter\": true,\n" - + " \"queryId\": \"EXAMPLE_QUERY_ID\",\n" - + " \"encryptionMaterial\": {\n" - + " \"queryStageMasterKey\": \"EXAMPLE_QUERY_STAGE_MASTER_KEY\",\n" - + " \"queryId\": \"EXAMPLE_QUERY_ID\",\n" - + " \"smkId\": 123\n" - + " },\n" - + " \"stageInfo\": {\n" - + " \"locationType\": \"S3\",\n" - + " \"location\": \"stage/location/foo/\",\n" - + " \"path\": \"tables/19805757505/\",\n" - + " \"region\": \"us-west-2\",\n" - + " \"storageAccount\": null,\n" - + " \"isClientSideEncrypted\": true,\n" - + " \"useS3RegionalUrl\": true,\n" - + " \"creds\": {\n" - + " \"AWS_KEY_ID\": \"EXAMPLE_AWS_KEY_ID\",\n" - + " \"AWS_SECRET_KEY\": \"EXAMPLE_AWS_SECRET_KEY\",\n" - + " \"AWS_TOKEN\": \"EXAMPLE_AWS_TOKEN\",\n" - + " \"AWS_ID\": \"EXAMPLE_AWS_ID\",\n" - + " \"AWS_KEY\": \"EXAMPLE_AWS_KEY\"\n" - + " },\n" - + " \"presignedUrl\": null,\n" - + " \"endPoint\": null\n" - + " },\n" - + " \"command\": \"UPLOAD\",\n" - + " \"kind\": null,\n" - + " \"operation\": \"Node\"\n" - + " },\n" - + " \"code\": null,\n" - + " \"message\": null,\n" - + " \"success\": true\n" - + "}"; - - private final String exampleAzureJsonString = - "{\n" - + " \"data\": {\n" - + " \"uploadInfo\": {\n" - + " \"locationType\": \"AZURE\",\n" - + " \"location\": \"EXAMPLE_LOCATION/\",\n" - + " \"path\": \"EXAMPLE_PATH/\",\n" - + " \"region\": \"westus\",\n" - + " \"storageAccount\": \"sfcdev2stage\",\n" - + " \"isClientSideEncrypted\": true,\n" - + " \"creds\": {\n" - + " \"AZURE_SAS_TOKEN\": \"EXAMPLE_AZURE_SAS_TOKEN\"\n" - + " },\n" - + " \"presignedUrl\": null,\n" - + " \"endPoint\": \"blob.core.windows.net\"\n" - + " },\n" - + " \"src_locations\": [\n" - + " \"/foo/orders_100.csv\"\n" - + " ],\n" - + " \"parallel\": 4,\n" - + " \"threshold\": 209715200,\n" - + " \"autoCompress\": true,\n" - + " \"overwrite\": false,\n" - + " \"sourceCompression\": \"auto_detect\",\n" - + " \"clientShowEncryptionParameter\": false,\n" - + " \"queryId\": \"EXAMPLE_QUERY_ID\",\n" - + " \"encryptionMaterial\": {\n" - + " \"queryStageMasterKey\": \"EXAMPLE_QUERY_STAGE_MASTER_KEY\",\n" - + " \"queryId\": \"EXAMPLE_QUERY_ID\",\n" - + " \"smkId\": 123\n" - + " },\n" - + " \"stageInfo\": {\n" - + " \"locationType\": \"AZURE\",\n" - + " \"location\": \"EXAMPLE_LOCATION/\",\n" - + " \"path\": \"EXAMPLE_PATH/\",\n" - + " \"region\": \"westus\",\n" - + " \"storageAccount\": \"EXAMPLE_STORAGE_ACCOUNT\",\n" - + " \"isClientSideEncrypted\": true,\n" - + " \"creds\": {\n" - + " \"AZURE_SAS_TOKEN\": \"EXAMPLE_AZURE_SAS_TOKEN\"\n" - + " },\n" - + " \"presignedUrl\": null,\n" - + " \"endPoint\": \"blob.core.windows.net\"\n" - + " },\n" - + " \"command\": \"UPLOAD\",\n" - + " \"kind\": null,\n" - + " \"operation\": \"Node\"\n" - + " },\n" - + " \"code\": null,\n" - + " \"message\": null,\n" - + " \"success\": true\n" - + "}"; - - private final String exampleGCSJsonString = - "{\n" - + " \"data\": {\n" - + " \"uploadInfo\": {\n" - + " \"locationType\": \"GCS\",\n" - + " \"location\": \"foo/tables/9224/\",\n" - + " \"path\": \"tables/9224/\",\n" - + " \"region\": \"US-WEST1\",\n" - + " \"storageAccount\": \"\",\n" - + " \"isClientSideEncrypted\": true,\n" - + " \"creds\": {},\n" - + " \"presignedUrl\": \"EXAMPLE_PRESIGNED_URL\",\n" - + " \"endPoint\": \"\"\n" - + " },\n" - + " \"src_locations\": [\n" - + " \"/foo/bart/orders_100.csv\"\n" - + " ],\n" - + " \"parallel\": 4,\n" - + " \"threshold\": 209715200,\n" - + " \"autoCompress\": true,\n" - + " \"overwrite\": false,\n" - + " \"sourceCompression\": \"auto_detect\",\n" - + " \"clientShowEncryptionParameter\": false,\n" - + " \"queryId\": \"EXAMPLE_QUERY_ID\",\n" - + " \"encryptionMaterial\": {\n" - + " \"queryStageMasterKey\": \"EXAMPLE_QUERY_STAGE_MASTER_KEY\",\n" - + " \"queryId\": \"EXAMPLE_QUERY_ID\",\n" - + " \"smkId\": 123\n" - + " },\n" - + " \"stageInfo\": {\n" - + " \"locationType\": \"GCS\",\n" - + " \"location\": \"foo/tables/9224/\",\n" - + " \"path\": \"tables/9224/\",\n" - + " \"region\": \"US-WEST1\",\n" - + " \"storageAccount\": \"\",\n" - + " \"isClientSideEncrypted\": true,\n" - + " \"creds\": {},\n" - + " \"presignedUrl\": \"EXAMPLE_PRESIGNED_URL\",\n" - + " \"endPoint\": \"\"\n" - + " },\n" - + " \"command\": \"UPLOAD\",\n" - + " \"kind\": null,\n" - + " \"operation\": \"Node\"\n" - + " },\n" - + " \"code\": null,\n" - + " \"message\": null,\n" - + " \"success\": true\n" - + "}"; - - protected JsonNode exampleS3JsonNode; - protected JsonNode exampleS3StageEndpointJsonNode; - protected JsonNode exampleAzureJsonNode; - protected JsonNode exampleGCSJsonNode; - protected List exampleNodes; + private static final ObjectMapper mapper = new ObjectMapper(); + + static JsonNode exampleS3JsonNode; + static JsonNode exampleS3StageEndpointJsonNode; + static JsonNode exampleAzureJsonNode; + static JsonNode exampleGCSJsonNode; + static JsonNode exampleGCSJsonNodeWithUseRegionalUrl; + static JsonNode exampleGCSJsonNodeWithEndPoint; + static List exampleNodes; + + private static JsonNode readJsonFromFile(String name) throws IOException { + try (InputStream is = + FileUploaderPrep.class.getResourceAsStream("/FileUploaderPrep/" + name + ".json")) { + return mapper.readTree(is); + } + } - @Before - public void setup() throws Exception { - exampleS3JsonNode = mapper.readTree(exampleS3JsonString); - exampleS3StageEndpointJsonNode = mapper.readTree(exampleS3JsonStringWithStageEndpoint); - exampleAzureJsonNode = mapper.readTree(exampleAzureJsonString); - exampleGCSJsonNode = mapper.readTree(exampleGCSJsonString); - exampleNodes = Arrays.asList(exampleS3JsonNode, exampleAzureJsonNode, exampleGCSJsonNode); + @BeforeClass + public static void setup() throws Exception { + exampleS3JsonNode = readJsonFromFile("exampleS3"); + exampleS3StageEndpointJsonNode = readJsonFromFile("exampleS3WithStageEndpoint"); + exampleAzureJsonNode = readJsonFromFile("exampleAzure"); + exampleGCSJsonNode = readJsonFromFile("exampleGCS"); + exampleGCSJsonNodeWithUseRegionalUrl = readJsonFromFile("exampleGCSWithUseRegionalUrl"); + exampleGCSJsonNodeWithEndPoint = readJsonFromFile("exampleGCSWithEndpoint"); + exampleNodes = + Arrays.asList( + exampleS3JsonNode, + exampleAzureJsonNode, + exampleGCSJsonNode, + exampleGCSJsonNodeWithUseRegionalUrl, + exampleGCSJsonNodeWithEndPoint); } } diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java index e23800e4e..f5fb7f719 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java @@ -3,6 +3,11 @@ */ package net.snowflake.client.jdbc; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -11,6 +16,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import net.snowflake.client.jdbc.cloud.storage.StageInfo; import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; import org.junit.Assert; @@ -265,6 +271,7 @@ public void testGetFileTransferMetadatasGCS() throws Exception { Assert.assertEquals(null, stageInfo.getEndPoint()); Assert.assertEquals(null, stageInfo.getStorageAccount()); Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(Optional.empty(), stageInfo.gcsCustomEndpoint()); // EncryptionMaterial check Assert.assertEquals("EXAMPLE_QUERY_ID", metadata.getEncryptionMaterial().getQueryId()); @@ -279,12 +286,42 @@ public void testGetFileTransferMetadatasGCS() throws Exception { Assert.assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); } + @Test + public void testGetFileTransferMetadataGCSWithUseRegionalUrl() throws Exception { + List metadataList = + SnowflakeFileTransferAgent.getFileTransferMetadatas(exampleGCSJsonNodeWithUseRegionalUrl); + Assert.assertEquals(1, metadataList.size()); + + SnowflakeFileTransferMetadataV1 metadata = + (SnowflakeFileTransferMetadataV1) metadataList.get(0); + + StageInfo stageInfo = metadata.getStageInfo(); + + assertTrue(stageInfo.getUseRegionalUrl()); + assertEquals(Optional.of("storage.us-west1.rep.googleapis.com"), stageInfo.gcsCustomEndpoint()); + } + + @Test + public void testGetFileTransferMetadataGCSWithEndPoint() throws Exception { + List metadataList = + SnowflakeFileTransferAgent.getFileTransferMetadatas(exampleGCSJsonNodeWithEndPoint); + Assert.assertEquals(1, metadataList.size()); + + SnowflakeFileTransferMetadataV1 metadata = + (SnowflakeFileTransferMetadataV1) metadataList.get(0); + + StageInfo stageInfo = metadata.getStageInfo(); + + assertFalse(stageInfo.getUseRegionalUrl()); + assertEquals(Optional.of("example.com"), stageInfo.gcsCustomEndpoint()); + } + @Test public void testGetFileTransferMetadatasUploadError() throws Exception { JsonNode downloadNode = mapper.readTree("{\"data\": {\"command\": \"DOWNLOAD\"}}"); try { SnowflakeFileTransferAgent.getFileTransferMetadatas(downloadNode); - Assert.assertTrue(false); + fail(); } catch (SnowflakeSQLException err) { Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); Assert.assertEquals( @@ -297,10 +334,10 @@ public void testGetFileTransferMetadatasEncryptionMaterialError() throws Excepti JsonNode garbageNode = mapper.readTree("{\"data\": {\"src_locations\": [1, 2]}}"); try { SnowflakeFileTransferAgent.getFileTransferMetadatas(garbageNode); - Assert.assertTrue(false); + fail(); } catch (SnowflakeSQLException err) { Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue( + assertTrue( err.getMessage().contains("JDBC driver internal error: Failed to parse the credentials")); } } @@ -312,11 +349,10 @@ public void testGetFileTransferMetadatasUnsupportedLocationError() throws Except foo.put("locationType", "LOCAL_FS"); try { SnowflakeFileTransferAgent.getFileTransferMetadatas(modifiedNode); - Assert.assertTrue(false); + fail(); } catch (SnowflakeSQLException err) { Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue( - err.getMessage().contains("JDBC driver internal error: This API only supports")); + assertTrue(err.getMessage().contains("JDBC driver internal error: This API only supports")); } } @@ -325,10 +361,10 @@ public void testGetFileTransferMetadatasSrcLocationsArrayError() throws JsonProc JsonNode garbageNode = mapper.readTree("{\"data\": {\"src_locations\": \"abc\"}}"); try { SnowflakeFileTransferAgent.getFileTransferMetadatas(garbageNode); - Assert.assertTrue(false); + fail(); } catch (SnowflakeSQLException err) { Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue( + assertTrue( err.getMessage().contains("JDBC driver internal error: src_locations must be an array")); } } @@ -340,10 +376,10 @@ public void testGetFileMetadatasEncryptionMaterialsException() { foo.put("encryptionMaterial", "[1, 2, 3]]"); try { SnowflakeFileTransferAgent.getFileTransferMetadatas(modifiedNode); - Assert.assertTrue(false); + fail(); } catch (SnowflakeSQLException err) { Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue(err.getMessage().contains("Failed to parse encryptionMaterial")); + assertTrue(err.getMessage().contains("Failed to parse encryptionMaterial")); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/StageInfoGcsCustomEndpointTest.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/StageInfoGcsCustomEndpointTest.java new file mode 100644 index 000000000..f8e00d7eb --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/StageInfoGcsCustomEndpointTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.jdbc.cloud.storage; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Optional; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class StageInfoGcsCustomEndpointTest { + private final String region; + private final boolean useRegionalUrl; + private final String endPoint; + private final Optional expectedHost; + + public StageInfoGcsCustomEndpointTest( + String region, boolean useRegionalUrl, String endPoint, Optional expectedHost) { + this.region = region; + this.useRegionalUrl = useRegionalUrl; + this.endPoint = endPoint; + this.expectedHost = expectedHost; + } + + @Test + public void shouldReturnEmptyGCSRegionalUrlWhenNotMeCentral1AndNotUseRegionalUrl() { + StageInfo stageInfo = + StageInfo.createStageInfo("GCS", "bla", new HashMap<>(), region, endPoint, "account", true); + stageInfo.setUseRegionalUrl(useRegionalUrl); + assertEquals(expectedHost, stageInfo.gcsCustomEndpoint()); + } + + @Parameterized.Parameters() + public static Object[][] data() { + return new Object[][] { + {"US-CENTRAL1", false, null, Optional.empty()}, + {"US-CENTRAL1", false, "", Optional.empty()}, + {"US-CENTRAL1", false, "null", Optional.empty()}, + {"US-CENTRAL1", false, " ", Optional.empty()}, + {"US-CENTRAL1", false, "example.com", Optional.of("example.com")}, + {"ME-CENTRAL2", false, null, Optional.of("storage.me-central2.rep.googleapis.com")}, + {"ME-CENTRAL2", true, null, Optional.of("storage.me-central2.rep.googleapis.com")}, + {"ME-CENTRAL2", true, "", Optional.of("storage.me-central2.rep.googleapis.com")}, + {"ME-CENTRAL2", true, " ", Optional.of("storage.me-central2.rep.googleapis.com")}, + {"ME-CENTRAL2", true, "example.com", Optional.of("example.com")}, + {"US-CENTRAL1", true, null, Optional.of("storage.us-central1.rep.googleapis.com")}, + {"US-CENTRAL1", true, "", Optional.of("storage.us-central1.rep.googleapis.com")}, + {"US-CENTRAL1", true, " ", Optional.of("storage.us-central1.rep.googleapis.com")}, + {"US-CENTRAL1", true, "null", Optional.of("storage.us-central1.rep.googleapis.com")}, + {"US-CENTRAL1", true, "example.com", Optional.of("example.com")}, + }; + } +} diff --git a/src/test/resources/FileUploaderPrep/exampleAzure.json b/src/test/resources/FileUploaderPrep/exampleAzure.json new file mode 100644 index 000000000..a2b1835c3 --- /dev/null +++ b/src/test/resources/FileUploaderPrep/exampleAzure.json @@ -0,0 +1,51 @@ +{ + "data": { + "uploadInfo": { + "locationType": "AZURE", + "location": "EXAMPLE_LOCATION/", + "path": "EXAMPLE_PATH/", + "region": "westus", + "storageAccount": "sfcdev2stage", + "isClientSideEncrypted": true, + "creds": { + "AZURE_SAS_TOKEN": "EXAMPLE_AZURE_SAS_TOKEN" + }, + "presignedUrl": null, + "endPoint": "blob.core.windows.net" + }, + "src_locations": [ + "/foo/orders_100.csv" + ], + "parallel": 4, + "threshold": 209715200, + "autoCompress": true, + "overwrite": false, + "sourceCompression": "auto_detect", + "clientShowEncryptionParameter": false, + "queryId": "EXAMPLE_QUERY_ID", + "encryptionMaterial": { + "queryStageMasterKey": "EXAMPLE_QUERY_STAGE_MASTER_KEY", + "queryId": "EXAMPLE_QUERY_ID", + "smkId": 123 + }, + "stageInfo": { + "locationType": "AZURE", + "location": "EXAMPLE_LOCATION/", + "path": "EXAMPLE_PATH/", + "region": "westus", + "storageAccount": "EXAMPLE_STORAGE_ACCOUNT", + "isClientSideEncrypted": true, + "creds": { + "AZURE_SAS_TOKEN": "EXAMPLE_AZURE_SAS_TOKEN" + }, + "presignedUrl": null, + "endPoint": "blob.core.windows.net" + }, + "command": "UPLOAD", + "kind": null, + "operation": "Node" + }, + "code": null, + "message": null, + "success": true +} \ No newline at end of file diff --git a/src/test/resources/FileUploaderPrep/exampleGCS.json b/src/test/resources/FileUploaderPrep/exampleGCS.json new file mode 100644 index 000000000..8cd605f1c --- /dev/null +++ b/src/test/resources/FileUploaderPrep/exampleGCS.json @@ -0,0 +1,47 @@ +{ + "data": { + "uploadInfo": { + "locationType": "GCS", + "location": "foo/tables/9224/", + "path": "tables/9224/", + "region": "US-WEST1", + "storageAccount": "", + "isClientSideEncrypted": true, + "creds": {}, + "presignedUrl": "EXAMPLE_PRESIGNED_URL", + "endPoint": "" + }, + "src_locations": [ + "/foo/bart/orders_100.csv" + ], + "parallel": 4, + "threshold": 209715200, + "autoCompress": true, + "overwrite": false, + "sourceCompression": "auto_detect", + "clientShowEncryptionParameter": false, + "queryId": "EXAMPLE_QUERY_ID", + "encryptionMaterial": { + "queryStageMasterKey": "EXAMPLE_QUERY_STAGE_MASTER_KEY", + "queryId": "EXAMPLE_QUERY_ID", + "smkId": 123 + }, + "stageInfo": { + "locationType": "GCS", + "location": "foo/tables/9224/", + "path": "tables/9224/", + "region": "US-WEST1", + "storageAccount": "", + "isClientSideEncrypted": true, + "creds": {}, + "presignedUrl": "EXAMPLE_PRESIGNED_URL", + "endPoint": "" + }, + "command": "UPLOAD", + "kind": null, + "operation": "Node" + }, + "code": null, + "message": null, + "success": true +} \ No newline at end of file diff --git a/src/test/resources/FileUploaderPrep/exampleGCSWithEndpoint.json b/src/test/resources/FileUploaderPrep/exampleGCSWithEndpoint.json new file mode 100644 index 000000000..8ba946c76 --- /dev/null +++ b/src/test/resources/FileUploaderPrep/exampleGCSWithEndpoint.json @@ -0,0 +1,47 @@ +{ + "data": { + "uploadInfo": { + "locationType": "GCS", + "location": "foo/tables/9224/", + "path": "tables/9224/", + "region": "US-WEST1", + "storageAccount": "", + "isClientSideEncrypted": true, + "creds": {}, + "presignedUrl": "EXAMPLE_PRESIGNED_URL", + "endPoint": "example.com" + }, + "src_locations": [ + "/foo/bart/orders_100.csv" + ], + "parallel": 4, + "threshold": 209715200, + "autoCompress": true, + "overwrite": false, + "sourceCompression": "auto_detect", + "clientShowEncryptionParameter": false, + "queryId": "EXAMPLE_QUERY_ID", + "encryptionMaterial": { + "queryStageMasterKey": "EXAMPLE_QUERY_STAGE_MASTER_KEY", + "queryId": "EXAMPLE_QUERY_ID", + "smkId": 123 + }, + "stageInfo": { + "locationType": "GCS", + "location": "foo/tables/9224/", + "path": "tables/9224/", + "region": "US-WEST1", + "storageAccount": "", + "isClientSideEncrypted": true, + "creds": {}, + "presignedUrl": "EXAMPLE_PRESIGNED_URL", + "endPoint": "example.com" + }, + "command": "UPLOAD", + "kind": null, + "operation": "Node" + }, + "code": null, + "message": null, + "success": true +} \ No newline at end of file diff --git a/src/test/resources/FileUploaderPrep/exampleGCSWithUseRegionalUrl.json b/src/test/resources/FileUploaderPrep/exampleGCSWithUseRegionalUrl.json new file mode 100644 index 000000000..79f4dc678 --- /dev/null +++ b/src/test/resources/FileUploaderPrep/exampleGCSWithUseRegionalUrl.json @@ -0,0 +1,49 @@ +{ + "data": { + "uploadInfo": { + "locationType": "GCS", + "useRegionalUrl": true, + "location": "foo/tables/9224/", + "path": "tables/9224/", + "region": "US-WEST1", + "storageAccount": "", + "isClientSideEncrypted": true, + "creds": {}, + "presignedUrl": "EXAMPLE_PRESIGNED_URL", + "endPoint": "" + }, + "src_locations": [ + "/foo/bart/orders_100.csv" + ], + "parallel": 4, + "threshold": 209715200, + "autoCompress": true, + "overwrite": false, + "sourceCompression": "auto_detect", + "clientShowEncryptionParameter": false, + "queryId": "EXAMPLE_QUERY_ID", + "encryptionMaterial": { + "queryStageMasterKey": "EXAMPLE_QUERY_STAGE_MASTER_KEY", + "queryId": "EXAMPLE_QUERY_ID", + "smkId": 123 + }, + "stageInfo": { + "locationType": "GCS", + "useRegionalUrl": true, + "location": "foo/tables/9224/", + "path": "tables/9224/", + "region": "US-WEST1", + "storageAccount": "", + "isClientSideEncrypted": true, + "creds": {}, + "presignedUrl": "EXAMPLE_PRESIGNED_URL", + "endPoint": "" + }, + "command": "UPLOAD", + "kind": null, + "operation": "Node" + }, + "code": null, + "message": null, + "success": true +} \ No newline at end of file diff --git a/src/test/resources/FileUploaderPrep/exampleS3.json b/src/test/resources/FileUploaderPrep/exampleS3.json new file mode 100644 index 000000000..eadc166d8 --- /dev/null +++ b/src/test/resources/FileUploaderPrep/exampleS3.json @@ -0,0 +1,60 @@ +{ + "data": { + "uploadInfo": { + "locationType": "S3", + "location": "example/location", + "path": "tables/19805757505/", + "region": "us-west-2", + "storageAccount": null, + "isClientSideEncrypted": true, + "creds": { + "AWS_KEY_ID": "EXAMPLE_AWS_KEY_ID", + "AWS_SECRET_KEY": "EXAMPLE_AWS_SECRET_KEY", + "AWS_TOKEN": "EXAMPLE_AWS_TOKEN", + "AWS_ID": "EXAMPLE_AWS_ID", + "AWS_KEY": "EXAMPLE_AWS_KEY" + }, + "presignedUrl": null, + "endPoint": null + }, + "src_locations": [ + "/tmp/files/orders_100.csv" + ], + "parallel": 4, + "threshold": 209715200, + "autoCompress": true, + "overwrite": false, + "sourceCompression": "auto_detect", + "clientShowEncryptionParameter": true, + "queryId": "EXAMPLE_QUERY_ID", + "encryptionMaterial": { + "queryStageMasterKey": "EXAMPLE_QUERY_STAGE_MASTER_KEY", + "queryId": "EXAMPLE_QUERY_ID", + "smkId": 123 + }, + "stageInfo": { + "locationType": "S3", + "location": "stage/location/foo/", + "path": "tables/19805757505/", + "region": "us-west-2", + "storageAccount": null, + "isClientSideEncrypted": true, + "useS3RegionalUrl": true, + "creds": { + "AWS_KEY_ID": "EXAMPLE_AWS_KEY_ID", + "AWS_SECRET_KEY": "EXAMPLE_AWS_SECRET_KEY", + "AWS_TOKEN": "EXAMPLE_AWS_TOKEN", + "AWS_ID": "EXAMPLE_AWS_ID", + "AWS_KEY": "EXAMPLE_AWS_KEY" + }, + "presignedUrl": null, + "endPoint": null + }, + "command": "UPLOAD", + "kind": null, + "operation": "Node" + }, + "code": null, + "message": null, + "success": true +} \ No newline at end of file diff --git a/src/test/resources/FileUploaderPrep/exampleS3WithStageEndpoint.json b/src/test/resources/FileUploaderPrep/exampleS3WithStageEndpoint.json new file mode 100644 index 000000000..32b8a66a1 --- /dev/null +++ b/src/test/resources/FileUploaderPrep/exampleS3WithStageEndpoint.json @@ -0,0 +1,59 @@ +{ + "data": { + "uploadInfo": { + "locationType": "S3", + "location": "example/location", + "path": "tables/19805757505/", + "region": "us-west-2", + "storageAccount": null, + "isClientSideEncrypted": true, + "creds": { + "AWS_KEY_ID": "EXAMPLE_AWS_KEY_ID", + "AWS_SECRET_KEY": "EXAMPLE_AWS_SECRET_KEY", + "AWS_TOKEN": "EXAMPLE_AWS_TOKEN", + "AWS_ID": "EXAMPLE_AWS_ID", + "AWS_KEY": "EXAMPLE_AWS_KEY" + }, + "presignedUrl": null, + "endPoint": null + }, + "src_locations": [ + "/tmp/files/orders_100.csv" + ], + "parallel": 4, + "threshold": 209715200, + "autoCompress": true, + "overwrite": false, + "sourceCompression": "auto_detect", + "clientShowEncryptionParameter": true, + "queryId": "EXAMPLE_QUERY_ID", + "encryptionMaterial": { + "queryStageMasterKey": "EXAMPLE_QUERY_STAGE_MASTER_KEY", + "queryId": "EXAMPLE_QUERY_ID", + "smkId": 123 + }, + "stageInfo": { + "locationType": "S3", + "location": "stage/location/foo/", + "path": "tables/19805757505/", + "region": "us-west-2", + "storageAccount": null, + "isClientSideEncrypted": true, + "creds": { + "AWS_KEY_ID": "EXAMPLE_AWS_KEY_ID", + "AWS_SECRET_KEY": "EXAMPLE_AWS_SECRET_KEY", + "AWS_TOKEN": "EXAMPLE_AWS_TOKEN", + "AWS_ID": "EXAMPLE_AWS_ID", + "AWS_KEY": "EXAMPLE_AWS_KEY" + }, + "presignedUrl": null, + "endPoint": "s3-fips.us-east-1.amazonaws.com" + }, + "command": "UPLOAD", + "kind": null, + "operation": "Node" + }, + "code": null, + "message": null, + "success": true +} \ No newline at end of file From 930f419cc95ce8c7d616d5476b1a87bd45678dfb Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:19:58 +0100 Subject: [PATCH 49/53] SNOW-1787626: Fix flaky test ConnectionLatestIT.testAsyncQueryOpenAndCloseConnection (#1974) --- .../client/jdbc/ConnectionLatestIT.java | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index 4dbbcb021..30ff6728f 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -274,7 +274,6 @@ public void testAsyncQueryOpenAndCloseConnection() throws SQLException, IOException, InterruptedException { // open connection and run asynchronous query String queryID = null; - QueryStatusV2 statusV2 = null; try (Connection con = getConnection(); Statement statement = con.createStatement(); ResultSet rs1 = @@ -288,7 +287,7 @@ public void testAsyncQueryOpenAndCloseConnection() await() .atMost(Duration.ofSeconds(5)) .until(() -> sfrs.getStatusV2().getStatus(), not(equalTo(QueryStatus.NO_DATA))); - statusV2 = sfrs.getStatusV2(); + QueryStatusV2 statusV2 = sfrs.getStatusV2(); // Query should take 60 seconds so should be running assertEquals(QueryStatus.RUNNING, statusV2.getStatus()); assertEquals(QueryStatus.RUNNING.name(), statusV2.getName()); @@ -305,7 +304,7 @@ public void testAsyncQueryOpenAndCloseConnection() assertEquals(SqlState.INVALID_PARAMETER_VALUE, e.getSQLState()); } try (ResultSet rs = con.unwrap(SnowflakeConnection.class).createResultSet(queryID)) { - statusV2 = rs.unwrap(SnowflakeResultSet.class).getStatusV2(); + QueryStatusV2 statusV2 = rs.unwrap(SnowflakeResultSet.class).getStatusV2(); // Assert status of query is a success assertEquals(QueryStatus.SUCCESS, statusV2.getStatus()); assertEquals("No error reported", statusV2.getErrorMessage()); @@ -318,27 +317,16 @@ public void testAsyncQueryOpenAndCloseConnection() .unwrap(SnowflakeStatement.class) .executeAsyncQuery("select * from nonexistentTable")) { Thread.sleep(100); - statusV2 = rs1.unwrap(SnowflakeResultSet.class).getStatusV2(); - // when GS response is slow, allow up to 1 second of retries to get final query status SnowflakeResultSet sfrs1 = rs1.unwrap(SnowflakeResultSet.class); await() .atMost(Duration.ofSeconds(10)) - .until( - () -> { - QueryStatus qs = sfrs1.getStatusV2().getStatus(); - return !(qs == QueryStatus.NO_DATA || qs == QueryStatus.RUNNING); - }); - // If GS response is too slow to return data, do nothing to avoid flaky test failure. If - // response has returned, - // assert it is the error message that we are expecting. - if (statusV2.getStatus() != QueryStatus.NO_DATA) { - assertEquals(QueryStatus.FAILED_WITH_ERROR, statusV2.getStatus()); - assertEquals(2003, statusV2.getErrorCode()); - assertEquals( - "SQL compilation error:\n" - + "Object 'NONEXISTENTTABLE' does not exist or not authorized.", - statusV2.getErrorMessage()); - } + .until(() -> sfrs1.getStatusV2().getStatus() == QueryStatus.FAILED_WITH_ERROR); + statusV2 = sfrs1.getStatusV2(); + assertEquals(2003, statusV2.getErrorCode()); + assertEquals( + "SQL compilation error:\n" + + "Object 'NONEXISTENTTABLE' does not exist or not authorized.", + statusV2.getErrorMessage()); } } } From ecccc3613f35dc229f9cfbb670bded1c0db6ea30 Mon Sep 17 00:00:00 2001 From: Dominik Przybysz <132913826+sfc-gh-dprzybysz@users.noreply.github.com> Date: Tue, 26 Nov 2024 07:56:29 +0100 Subject: [PATCH 50/53] SNOW-1825712: Update GH actions versions (#1975) --- .github/workflows/build-test.yml | 10 +++++----- .github/workflows/check-style.yml | 2 +- .github/workflows/jira_close.yml | 2 +- .github/workflows/jira_issue.yml | 2 +- .github/workflows/snyk-issue.yml | 2 +- .github/workflows/snyk-pr.yml | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 2607c5d46..ef331f720 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -29,7 +29,7 @@ jobs: name: Build runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Build shell: bash env: @@ -53,7 +53,7 @@ jobs: java-version: ${{ matrix.runConfig.javaVersion }} distribution: 'temurin' cache: maven - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.7' architecture: 'x64' @@ -83,7 +83,7 @@ jobs: java-version: ${{ matrix.runConfig.javaVersion }} distribution: 'temurin' cache: maven - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.7' - name: Install Homebrew Bash @@ -110,7 +110,7 @@ jobs: category: ['TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader', 'TestCategoryOthers', 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic', 'TestCategoryFips'] additionalMavenProfile: ['', '-Dthin-jar'] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Tests shell: bash env: @@ -132,7 +132,7 @@ jobs: category: ['TestCategoryOthers', 'TestCategoryConnection,TestCategoryStatement', 'TestCategoryCore,TestCategoryLoader,TestCategoryResultSet'] is_old_driver: ['true'] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Tests shell: bash env: diff --git a/.github/workflows/check-style.yml b/.github/workflows/check-style.yml index 221651298..d26f41865 100644 --- a/.github/workflows/check-style.yml +++ b/.github/workflows/check-style.yml @@ -9,7 +9,7 @@ jobs: name: Check Style runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Check Style shell: bash run: mvn clean validate --batch-mode --show-version -P check-style diff --git a/.github/workflows/jira_close.yml b/.github/workflows/jira_close.yml index dfcb8bc73..0dacf7fab 100644 --- a/.github/workflows/jira_close.yml +++ b/.github/workflows/jira_close.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: snowflakedb/gh-actions ref: jira_v1 diff --git a/.github/workflows/jira_issue.yml b/.github/workflows/jira_issue.yml index 943ad70aa..92501da8f 100644 --- a/.github/workflows/jira_issue.yml +++ b/.github/workflows/jira_issue.yml @@ -14,7 +14,7 @@ jobs: if: ((github.event_name == 'issue_comment' && github.event.comment.body == 'recreate jira' && github.event.comment.user.login == 'sfc-gh-mkeller') || (github.event_name == 'issues' && github.event.pull_request.user.login != 'whitesource-for-github-com[bot]')) steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: snowflakedb/gh-actions ref: jira_v1 diff --git a/.github/workflows/snyk-issue.yml b/.github/workflows/snyk-issue.yml index 7b58bb12a..1e36dae35 100644 --- a/.github/workflows/snyk-issue.yml +++ b/.github/workflows/snyk-issue.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout action - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: snowflakedb/whitesource-actions token: ${{ secrets.WHITESOURCE_ACTION_TOKEN }} diff --git a/.github/workflows/snyk-pr.yml b/.github/workflows/snyk-pr.yml index 5fc21951b..0c101e391 100644 --- a/.github/workflows/snyk-pr.yml +++ b/.github/workflows/snyk-pr.yml @@ -15,13 +15,13 @@ jobs: if: ${{ github.event.pull_request.user.login == 'sfc-gh-snyk-sca-sa' }} steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.ref }} fetch-depth: 0 - name: checkout action - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: snowflakedb/whitesource-actions token: ${{ secrets.WHITESOURCE_ACTION_TOKEN }} From f1038e6e42918b0bd044662f76be814b7852e473 Mon Sep 17 00:00:00 2001 From: Laurent Goujon Date: Tue, 26 Nov 2024 01:09:03 -0800 Subject: [PATCH 51/53] SNOW-1747516: Fix native libraries relocation (#1927) --- FIPS/pom.xml | 28 ++++++++++++++++++++++++++++ parent-pom.xml | 1 + pom.xml | 28 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/FIPS/pom.xml b/FIPS/pom.xml index 0b874551d..b8efd8c05 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -510,6 +510,22 @@ io.grpc ${shadeBase}.grpc + + META-INF.native.io_grpc_netty_shaded_netty_tcnative + META-INF.native.${shadeNativeBase}_grpc_netty_shaded_netty_tcnative + + + META-INF.native.libio_grpc_netty_shaded_netty_tcnative + META-INF.native.lib${shadeNativeBase}_grpc_netty_shaded_netty_tcnative + + + META-INF.native.io_grpc_netty_shaded_netty_transport_native_epoll + META-INF.native.${shadeNativeBase}_grpc_netty_shaded_netty_transport_native_epoll + + + META-INF.native.libio_grpc_netty_shaded_netty_transport_native_epoll + META-INF.native.lib${shadeNativeBase}_grpc_netty_shaded_netty_transport_native_epoll + org.checkerframework ${shadeBase}.org.checkerframework @@ -526,6 +542,18 @@ org.conscrypt ${shadeBase}.org.conscrypt + + conscrypt_openjdk_jni + ${shadeNativeBase}_conscrypt_openjdk_jni + + + META-INF.native.conscrypt_openjdk_jni + META-INF.native.${shadeNativeBase}_conscrypt_openjdk_jni + + + META-INF.native.libconscrypt_openjdk_jni + META-INF.native.lib${shadeNativeBase}_conscrypt_openjdk_jni + opencensus ${shadeBase}.opencensus diff --git a/parent-pom.xml b/parent-pom.xml index d69c49cbf..1e5aac29c 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -75,6 +75,7 @@ UTF-8 net/snowflake/client/jdbc/internal net.snowflake.client.jdbc.internal + net_snowflake_client_jdbc_internal 2.0.13 5.1.4 net.snowflake.client.category.AllTestCategory diff --git a/pom.xml b/pom.xml index 45e57505b..0f34e2953 100644 --- a/pom.xml +++ b/pom.xml @@ -943,6 +943,22 @@ io.grpc ${shadeBase}.grpc + + META-INF.native.io_grpc_netty_shaded_netty_tcnative + META-INF.native.${shadeNativeBase}_grpc_netty_shaded_netty_tcnative + + + META-INF.native.libio_grpc_netty_shaded_netty_tcnative + META-INF.native.lib${shadeNativeBase}_grpc_netty_shaded_netty_tcnative + + + META-INF.native.io_grpc_netty_shaded_netty_transport_native_epoll + META-INF.native.${shadeNativeBase}_grpc_netty_shaded_netty_transport_native_epoll + + + META-INF.native.libio_grpc_netty_shaded_netty_transport_native_epoll + META-INF.native.lib${shadeNativeBase}_grpc_netty_shaded_netty_transport_native_epoll + org.checkerframework ${shadeBase}.org.checkerframework @@ -959,6 +975,18 @@ org.conscrypt ${shadeBase}.org.conscrypt + + conscrypt_openjdk_jni + ${shadeNativeBase}_conscrypt_openjdk_jni + + + META-INF.native.conscrypt_openjdk_jni + META-INF.native.${shadeNativeBase}_conscrypt_openjdk_jni + + + META-INF.native.libconscrypt_openjdk_jni + META-INF.native.lib${shadeNativeBase}_conscrypt_openjdk_jni + opencensus ${shadeBase}.opencensus From 7fd70cdc5fbb31798b56b1335de87ed3b56b3725 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Tue, 26 Nov 2024 10:25:02 +0100 Subject: [PATCH 52/53] SNOW-1689931 Adding flag to skip token file permission verification (#1959) --- .../config/SFConnectionConfigParser.java | 86 +++++++++++-------- .../snowflake/client/jdbc/SnowflakeUtil.java | 16 ++++ .../config/SFConnectionConfigParserTest.java | 69 ++++++++++++++- 3 files changed, 132 insertions(+), 39 deletions(-) diff --git a/src/main/java/net/snowflake/client/config/SFConnectionConfigParser.java b/src/main/java/net/snowflake/client/config/SFConnectionConfigParser.java index 35698c557..1da9f766a 100644 --- a/src/main/java/net/snowflake/client/config/SFConnectionConfigParser.java +++ b/src/main/java/net/snowflake/client/config/SFConnectionConfigParser.java @@ -1,5 +1,6 @@ package net.snowflake.client.config; +import static net.snowflake.client.jdbc.SnowflakeUtil.convertSystemGetEnvToBooleanValue; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetEnv; import com.fasterxml.jackson.dataformat.toml.TomlMapper; @@ -34,6 +35,53 @@ public class SFConnectionConfigParser { "SNOWFLAKE_DEFAULT_CONNECTION_NAME"; public static final String DEFAULT = "default"; public static final String SNOWFLAKE_TOKEN_FILE_PATH = "/snowflake/session/token"; + public static final String SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION = + "SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION"; + + public static ConnectionParameters buildConnectionParameters() throws SnowflakeSQLException { + String defaultConnectionName = + Optional.ofNullable(systemGetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY)).orElse(DEFAULT); + Map fileConnectionConfiguration = + loadDefaultConnectionConfiguration(defaultConnectionName); + + if (fileConnectionConfiguration != null && !fileConnectionConfiguration.isEmpty()) { + Properties connectionProperties = new Properties(); + connectionProperties.putAll(fileConnectionConfiguration); + + String url = createUrl(fileConnectionConfiguration); + logger.debug("Url created using parameters from connection configuration file: {}", url); + + if ("oauth".equals(fileConnectionConfiguration.get("authenticator")) + && fileConnectionConfiguration.get("token") == null) { + Path path = + Paths.get( + Optional.ofNullable(fileConnectionConfiguration.get("token_file_path")) + .orElse(SNOWFLAKE_TOKEN_FILE_PATH)); + logger.debug("Token used in connect is read from file: {}", path); + try { + boolean shouldSkipTokenFilePermissionsVerification = + convertSystemGetEnvToBooleanValue(SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION, false); + if (!shouldSkipTokenFilePermissionsVerification) { + verifyFilePermissionSecure(path); + } else { + logger.debug("Skip token file permissions verification"); + } + String token = new String(Files.readAllBytes(path), Charset.defaultCharset()); + if (!token.isEmpty()) { + putPropertyIfNotNull(connectionProperties, "token", token.trim()); + } else { + throw new SnowflakeSQLException( + "Non-empty token must be set when the authenticator type is OAUTH"); + } + } catch (Exception ex) { + throw new SnowflakeSQLException(ex, "There is a problem during reading token from file"); + } + } + return new ConnectionParameters(url, connectionProperties); + } else { + return null; + } + } private static Map loadDefaultConnectionConfiguration( String defaultConnectionName) throws SnowflakeSQLException { @@ -88,44 +136,6 @@ private static void verifyFilePermissionSecure(Path configFilePath) } } - public static ConnectionParameters buildConnectionParameters() throws SnowflakeSQLException { - String defaultConnectionName = - Optional.ofNullable(systemGetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY)).orElse(DEFAULT); - Map fileConnectionConfiguration = - loadDefaultConnectionConfiguration(defaultConnectionName); - - if (fileConnectionConfiguration != null && !fileConnectionConfiguration.isEmpty()) { - Properties conectionProperties = new Properties(); - conectionProperties.putAll(fileConnectionConfiguration); - - String url = createUrl(fileConnectionConfiguration); - logger.debug("Url created using parameters from connection configuration file: {}", url); - - if ("oauth".equals(fileConnectionConfiguration.get("authenticator")) - && fileConnectionConfiguration.get("token") == null) { - Path path = - Paths.get( - Optional.ofNullable(fileConnectionConfiguration.get("token_file_path")) - .orElse(SNOWFLAKE_TOKEN_FILE_PATH)); - logger.debug("Token used in connect is read from file: {}", path); - try { - verifyFilePermissionSecure(path); - String token = new String(Files.readAllBytes(path), Charset.defaultCharset()); - if (!token.isEmpty()) { - putPropertyIfNotNull(conectionProperties, "token", token.trim()); - } else { - logger.warn("The token has empty value"); - } - } catch (Exception ex) { - throw new SnowflakeSQLException(ex, "There is a problem during reading token from file"); - } - } - return new ConnectionParameters(url, conectionProperties); - } else { - return null; - } - } - private static String createUrl(Map fileConnectionConfiguration) throws SnowflakeSQLException { Optional maybeAccount = Optional.ofNullable(fileConnectionConfiguration.get("account")); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index 635384972..8e9a683a0 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -837,6 +837,22 @@ public static boolean convertSystemPropertyToBooleanValue( } return defaultValue; } + /** + * Helper function to convert environment variable to boolean + * + * @param envVariableKey property name of the environment variable + * @param defaultValue default value used + * @return the value of the environment variable as boolean, else the default value + */ + @SnowflakeJdbcInternalApi + public static boolean convertSystemGetEnvToBooleanValue( + String envVariableKey, boolean defaultValue) { + String environmentVariableValue = systemGetEnv(envVariableKey); + if (environmentVariableValue != null) { + return Boolean.parseBoolean(environmentVariableValue); + } + return defaultValue; + } @SnowflakeJdbcInternalApi public static T mapSFExceptionToSQLException(ThrowingCallable action) diff --git a/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java b/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java index 01da714e5..bfb30f645 100644 --- a/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java +++ b/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java @@ -1,5 +1,6 @@ package net.snowflake.client.config; +import static net.snowflake.client.config.SFConnectionConfigParser.SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION; import static net.snowflake.client.config.SFConnectionConfigParser.SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY; import static net.snowflake.client.config.SFConnectionConfigParser.SNOWFLAKE_HOME_KEY; import static org.junit.Assert.assertEquals; @@ -17,8 +18,11 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import net.snowflake.client.RunningNotOnLinuxMac; @@ -32,20 +36,36 @@ public class SFConnectionConfigParserTest { + private static final List ENV_VARIABLES_KEYS = + new ArrayList<>( + Arrays.asList( + SNOWFLAKE_HOME_KEY, + SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY, + SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION)); private Path tempPath = null; private TomlMapper tomlMapper = new TomlMapper(); + private Map envVariables = new HashMap(); @Before public void setUp() throws IOException { tempPath = Files.createTempDirectory(".snowflake"); + ENV_VARIABLES_KEYS.stream() + .forEach( + key -> { + if (SnowflakeUtil.systemGetEnv(key) != null) { + envVariables.put(key, SnowflakeUtil.systemGetEnv(key)); + } + }); } @After public void close() throws IOException { SnowflakeUtil.systemUnsetEnv(SNOWFLAKE_HOME_KEY); SnowflakeUtil.systemUnsetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY); + SnowflakeUtil.systemUnsetEnv(SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION); Files.walk(tempPath).map(Path::toFile).forEach(File::delete); Files.delete(tempPath); + envVariables.forEach((key, value) -> SnowflakeUtil.systemSetEnv(key, value)); } @Test @@ -103,6 +123,21 @@ public void testThrowErrorWhenWrongPermissionsForTokenFile() throws IOException SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters()); } + @Test + public void testNoThrowErrorWhenWrongPermissionsForTokenFileButSkippingFlagIsEnabled() + throws SnowflakeSQLException, IOException { + SnowflakeUtil.systemSetEnv(SNOWFLAKE_HOME_KEY, tempPath.toString()); + SnowflakeUtil.systemSetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY, "default"); + SnowflakeUtil.systemSetEnv(SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION, "true"); + File tokenFile = new File(Paths.get(tempPath.toString(), "token").toUri()); + prepareConnectionConfigurationTomlFile( + Collections.singletonMap("token_file_path", tokenFile.toString()), true, false); + + ConnectionParameters data = SFConnectionConfigParser.buildConnectionParameters(); + assertNotNull(data); + assertEquals(tokenFile.toString(), data.getParams().get("token_file_path")); + } + @Test public void testLoadSFConnectionConfigWithHostConfigured() throws SnowflakeSQLException, IOException { @@ -133,6 +168,19 @@ public void shouldThrowExceptionIfNoneOfHostAndAccountIsSet() throws IOException SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters()); } + @Test + public void shouldThrowExceptionIfTokenIsNotSetForOauth() throws IOException { + SnowflakeUtil.systemSetEnv(SNOWFLAKE_HOME_KEY, tempPath.toString()); + SnowflakeUtil.systemSetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY, "default"); + SnowflakeUtil.systemSetEnv(SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION, "true"); + File tokenFile = new File(Paths.get(tempPath.toString(), "token").toUri()); + prepareConnectionConfigurationTomlFile( + Collections.singletonMap("token_file_path", tokenFile.toString()), true, false, ""); + + Assert.assertThrows( + SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters()); + } + private void prepareConnectionConfigurationTomlFile() throws IOException { prepareConnectionConfigurationTomlFile(null, true, true); } @@ -144,6 +192,16 @@ private void prepareConnectionConfigurationTomlFile(Map moreParameters) throws I private void prepareConnectionConfigurationTomlFile( Map moreParameters, boolean onlyUserPermissionConnection, boolean onlyUserPermissionToken) throws IOException { + prepareConnectionConfigurationTomlFile( + moreParameters, onlyUserPermissionConnection, onlyUserPermissionToken, "token_from_file"); + } + + private void prepareConnectionConfigurationTomlFile( + Map moreParameters, + boolean onlyUserPermissionConnection, + boolean onlyUserPermissionToken, + String token) + throws IOException { Path path = Paths.get(tempPath.toString(), "connections.toml"); Path filePath = createFilePathWithPermission(path, onlyUserPermissionConnection); File file = filePath.toFile(); @@ -166,7 +224,16 @@ private void prepareConnectionConfigurationTomlFile( createFilePathWithPermission( Paths.get(configurationParams.get("token_file_path").toString()), onlyUserPermissionToken); - Files.write(tokenFilePath, "token_from_file".getBytes()); + Files.write(tokenFilePath, token.getBytes()); + Path emptyTokenFilePath = + createFilePathWithPermission( + Paths.get( + configurationParams + .get("token_file_path") + .toString() + .replaceAll("token", "emptytoken")), + onlyUserPermissionToken); + Files.write(emptyTokenFilePath, "".getBytes()); } } From a20f2cff732c68b176351d7566f4743e79a65379 Mon Sep 17 00:00:00 2001 From: Antoni Stachowski Date: Wed, 27 Nov 2024 15:13:34 +0100 Subject: [PATCH 53/53] SNOW-1693588 Upgrade to JUnit5 (#1909) --- .github/workflows/build-test.yml | 35 +- FIPS/pom.xml | 14 + .../snowflake/client/AbstractDriverIT.java | 2 - .../client/ConditionalIgnoreRule.java | 125 ----- .../net/snowflake/client/DontRunOnGCP.java | 15 + .../client/DontRunOnGithubActions.java | 15 + .../net/snowflake/client/RunningOnGCP.java | 12 - .../client/RunningOnGithubActions.java | 11 - .../net/snowflake/client/RunningOnWinMac.java | 0 .../java/net/snowflake/client/TestUtil.java | 4 +- .../client/category/FipsTestSuite.java | 22 + .../client/category/TestCategoryFips.java | 3 - .../client/jdbc/ConnectionFipsIT.java | 33 +- TestOnly/pom.xml | 117 +++- ci/container/test_component.sh | 63 +-- ci/log_analyze_setup.sh | 4 +- ci/test.sh | 6 +- ci/test_windows.bat | 80 ++- parent-pom.xml | 131 ++++- pom.xml | 115 ++-- .../client/jdbc/SnowflakeDriverIT.java | 10 +- .../snowflake/client/AbstractDriverIT.java | 3 - .../net/snowflake/client/AssumptionUtils.java | 36 ++ .../client/ConditionalIgnoreRule.java | 125 ----- .../net/snowflake/client/RunningNotOnAWS.java | 12 - .../snowflake/client/RunningNotOnAzure.java | 12 - .../net/snowflake/client/RunningNotOnGCP.java | 12 - .../client/RunningNotOnGithubActionsMac.java | 16 - .../snowflake/client/RunningNotOnJava21.java | 13 - .../snowflake/client/RunningNotOnJava8.java | 13 - .../snowflake/client/RunningNotOnLinux.java | 9 - .../client/RunningNotOnLinuxMac.java | 13 - .../client/RunningNotOnTestaccount.java | 10 - .../net/snowflake/client/RunningNotOnWin.java | 9 - .../snowflake/client/RunningNotOnWinMac.java | 9 - .../client/RunningOnGithubAction.java | 15 - .../client/RunningOnTestaccount.java | 7 - .../net/snowflake/client/RunningOnWin.java | 9 - .../net/snowflake/client/SkipOnThinJar.java | 12 - .../java/net/snowflake/client/TestUtil.java | 16 +- .../annotations/DontRunOnGithubActions.java | 15 + .../client/annotations/DontRunOnJava21.java | 16 + .../client/annotations/DontRunOnJava8.java | 16 + .../annotations/DontRunOnTestaccount.java | 15 + .../client/annotations/DontRunOnThinJar.java | 15 + .../client/annotations/DontRunOnWindows.java | 16 + .../client/annotations/RunOnAWS.java | 15 + .../client/annotations/RunOnAzure.java | 15 + .../client/annotations/RunOnGCP.java | 15 + .../annotations/RunOnGithubActionsNotMac.java | 18 + .../client/annotations/RunOnLinux.java | 16 + .../client/annotations/RunOnLinuxOrMac.java | 16 + .../client/annotations/RunOnMac.java | 16 + .../RunOnTestaccountNotOnGithubActions.java | 17 + .../client/annotations/RunOnWindows.java | 16 + .../client/annotations/RunOnWindowsOrMac.java | 16 + .../client/category/TestCategoryArrow.java | 3 - .../category/TestCategoryConnection.java | 3 - .../client/category/TestCategoryCore.java | 3 - .../category/TestCategoryDiagnostic.java | 3 - .../client/category/TestCategoryLoader.java | 4 - .../client/category/TestCategoryOthers.java | 3 - .../category/TestCategoryResultSet.java | 3 - .../category/TestCategoryStatement.java | 3 - .../snowflake/client/category/TestTags.java | 17 + .../config/SFClientConfigParserTest.java | 14 +- .../config/SFConnectionConfigParserTest.java | 30 +- .../client/config/SFPermissionsTest.java | 86 ++- .../core/CoreUtilsMiscellaneousTest.java | 15 +- .../client/core/EventHandlerTest.java | 19 +- .../net/snowflake/client/core/EventTest.java | 27 +- .../core/ExecTimeTelemetryDataTest.java | 8 +- .../client/core/HttpUtilLatestIT.java | 16 +- .../client/core/IncidentUtilLatestIT.java | 38 +- .../client/core/OCSPCacheServerTest.java | 145 +++-- .../client/core/ObjectMapperTest.java | 72 +-- .../client/core/PrivateLinkDetectorTest.java | 56 +- .../client/core/QueryContextCacheTest.java | 4 +- .../client/core/SFArrowResultSetIT.java | 66 ++- .../client/core/SFLoginInputTest.java | 4 +- .../client/core/SFSessionPropertyTest.java | 10 +- .../client/core/SFTrustManagerIT.java | 99 ++-- .../SFTrustManagerMockitoMockLatestIT.java | 20 +- .../client/core/SFTrustManagerTest.java | 16 +- .../client/core/SQLInputOutputTest.java | 2 +- .../client/core/SecureStorageManagerTest.java | 18 +- .../core/SessionUtilExternalBrowserTest.java | 24 +- .../client/core/SessionUtilLatestIT.java | 16 +- .../client/core/SessionUtilTest.java | 24 +- .../client/core/SnowflakeMFACacheTest.java | 14 +- .../core/SqlInputTimestampUtilTest.java | 12 +- .../snowflake/client/core/StmtUtilTest.java | 8 +- .../snowflake/client/core/URLUtilTest.java | 8 +- .../core/arrow/ArrowResultUtilTest.java | 89 +-- .../client/core/arrow/BaseConverterTest.java | 16 +- .../arrow/BigIntToFixedConverterTest.java | 6 +- .../core/arrow/BigIntToTimeConverterTest.java | 36 +- .../BigIntToTimestampLTZConverterTest.java | 33 +- .../BigIntToTimestampNTZConverterTest.java | 65 +-- .../core/arrow/BitToBooleanConverterTest.java | 6 +- .../client/core/arrow/DateConverterTest.java | 56 +- .../core/arrow/DoubleToRealConverterTest.java | 6 +- .../core/arrow/IntToFixedConverterTest.java | 8 +- .../core/arrow/IntToTimeConverterTest.java | 42 +- .../arrow/SmallIntToFixedConverterTest.java | 8 +- ...FieldStructToTimestampTZConverterTest.java | 125 +++-- .../arrow/TinyIntToFixedConverterTest.java | 8 +- ...ieldStructToTimestampLTZConverterTest.java | 114 ++-- ...ieldStructToTimestampNTZConverterTest.java | 152 +++--- ...FieldStructToTimestampTZConverterTest.java | 36 +- .../arrow/VarBinaryToBinaryConverterTest.java | 6 +- .../core/arrow/VarCharConverterTest.java | 6 +- .../client/core/bind/BindExceptionTest.java | 4 +- .../core/json/BooleanConverterTest.java | 9 +- .../client/core/json/BytesConverterTest.java | 4 +- .../core/json/DateTimeConverterTest.java | 6 +- .../client/core/json/NumberConverterTest.java | 2 +- .../client/core/json/StringConverterTest.java | 8 +- .../client/jdbc/ArrowResultChunkTest.java | 2 +- .../snowflake/client/jdbc/BaseJDBCTest.java | 6 +- .../jdbc/BaseJDBCWithSharedConnectionIT.java | 15 +- .../client/jdbc/BaseWiremockTest.java | 38 +- .../snowflake/client/jdbc/BindUploaderIT.java | 32 +- .../client/jdbc/BindUploaderLatestIT.java | 28 +- ...ngAndInsertingStructuredTypesLatestIT.java | 131 +++-- .../snowflake/client/jdbc/BindingDataIT.java | 127 +++-- .../client/jdbc/BindingDataLatestIT.java | 21 +- .../client/jdbc/CallableStatementIT.java | 76 +-- .../client/jdbc/CallableStatementITBase.java | 48 ++ .../jdbc/CallableStatementLatestIT.java | 28 +- .../ChunkDownloaderS3RetryUrlLatestIT.java | 14 +- .../jdbc/ClientMemoryLimitParallelIT.java | 27 +- .../jdbc/CompressedStreamFactoryTest.java | 6 +- .../client/jdbc/ConnectStringParseTest.java | 4 +- .../jdbc/ConnectionAlreadyClosedIT.java | 8 +- .../jdbc/ConnectionFeatureNotSupportedIT.java | 8 +- .../snowflake/client/jdbc/ConnectionIT.java | 66 ++- .../client/jdbc/ConnectionLatestIT.java | 123 +++-- .../client/jdbc/ConnectionManual.java | 2 +- .../client/jdbc/ConnectionPoolingIT.java | 18 +- .../client/jdbc/ConnectionWithOCSPModeIT.java | 31 +- .../client/jdbc/CustomProxyLatestIT.java | 80 ++- .../client/jdbc/DatabaseMetaDataIT.java | 27 +- .../jdbc/DatabaseMetaDataInternalIT.java | 44 +- .../DatabaseMetaDataInternalLatestIT.java | 29 +- .../client/jdbc/DatabaseMetaDataLatestIT.java | 51 +- .../DatabaseMetaDataResultSetLatestIT.java | 21 +- .../jdbc/DatabaseMetaDataResultsetIT.java | 14 +- .../client/jdbc/DellBoomiCloudIT.java | 14 +- .../FileConnectionConfigurationLatestIT.java | 17 +- .../jdbc/FileUploaderExpandFileNamesTest.java | 33 +- .../client/jdbc/FileUploaderLatestIT.java | 92 ++-- ...UploaderMimeTypeToCompressionTypeTest.java | 66 +-- .../client/jdbc/FileUploaderPrep.java | 7 +- .../jdbc/FileUploaderSessionlessTest.java | 210 +++---- .../snowflake/client/jdbc/GCPLargeResult.java | 34 +- .../jdbc/GitRepositoryDownloadLatestIT.java | 25 +- .../client/jdbc/HeartbeatAsyncLatestIT.java | 23 +- .../snowflake/client/jdbc/HeartbeatIT.java | 36 +- .../client/jdbc/LobSizeLatestIT.java | 95 ++-- .../client/jdbc/MaxLobSizeLatestIT.java | 17 +- .../client/jdbc/MockConnectionTest.java | 19 +- .../client/jdbc/MultiStatementArrowIT.java | 6 +- .../client/jdbc/MultiStatementIT.java | 30 +- .../client/jdbc/MultiStatementLatestIT.java | 22 +- .../client/jdbc/OpenGroupCLIFuncIT.java | 16 +- .../client/jdbc/OpenGroupCLIFuncLatestIT.java | 8 +- .../client/jdbc/PreparedMultiStmtIT.java | 66 ++- .../client/jdbc/PreparedStatement0IT.java | 18 +- .../client/jdbc/PreparedStatement1IT.java | 212 ++++---- .../jdbc/PreparedStatement1LatestIT.java | 118 ++-- .../client/jdbc/PreparedStatement2IT.java | 226 ++++---- .../jdbc/PreparedStatement2LatestIT.java | 114 ++-- .../jdbc/PreparedStatementArrow1IT.java | 15 - .../jdbc/PreparedStatementArrow1LatestIT.java | 18 - .../jdbc/PreparedStatementArrow2IT.java | 15 - .../jdbc/PreparedStatementArrow2LatestIT.java | 18 - ...reparedStatementFeatureNotSupportedIT.java | 8 +- .../PreparedStatementLargeUpdateLatestIT.java | 17 +- .../snowflake/client/jdbc/ProxyLatestIT.java | 20 +- .../jdbc/PutFileWithSpaceIncludedIT.java | 24 +- .../client/jdbc/PutUnescapeBackslashIT.java | 12 +- .../client/jdbc/RestRequestTest.java | 57 +- .../jdbc/RestRequestWiremockLatestIT.java | 8 +- .../client/jdbc/ResultJsonParserV2Test.java | 6 +- .../snowflake/client/jdbc/ResultSet0IT.java | 38 +- .../client/jdbc/ResultSetAlreadyClosedIT.java | 12 +- .../ResultSetArrowForce0MultiTimeZone.java | 56 +- ...ResultSetArrowForceLTZMultiTimeZoneIT.java | 70 +-- .../ResultSetArrowForceTZMultiTimeZoneIT.java | 61 +-- .../client/jdbc/ResultSetArrowIT.java | 14 - .../client/jdbc/ResultSetArrowLatestIT.java | 18 - .../client/jdbc/ResultSetAsyncIT.java | 26 +- .../client/jdbc/ResultSetAsyncLatestIT.java | 10 +- .../jdbc/ResultSetFeatureNotSupportedIT.java | 8 +- .../snowflake/client/jdbc/ResultSetIT.java | 317 ++++++----- .../client/jdbc/ResultSetJsonVsArrowIT.java | 287 +++++----- .../jdbc/ResultSetJsonVsArrowMultiTZIT.java | 138 +++-- .../client/jdbc/ResultSetLatestIT.java | 293 +++++----- .../client/jdbc/ResultSetMultiTimeZoneIT.java | 213 +++++--- .../jdbc/ResultSetMultiTimeZoneLatestIT.java | 147 ++--- .../client/jdbc/ResultSetVectorLatestIT.java | 120 ++-- .../client/jdbc/SSOConnectionTest.java | 4 +- .../client/jdbc/ServiceNameTest.java | 2 +- .../client/jdbc/SessionUtilTest.java | 31 +- .../client/jdbc/SessionVariablesIT.java | 12 +- ...akeAzureClientHandleExceptionLatestIT.java | 176 +++--- .../jdbc/SnowflakeBasicDataSourceTest.java | 4 +- .../SnowflakeChunkDownloaderLatestIT.java | 18 +- .../client/jdbc/SnowflakeClobTest.java | 8 +- .../jdbc/SnowflakeConnectionV1Test.java | 2 +- .../SnowflakeDriverConnectionStressTest.java | 2 +- .../client/jdbc/SnowflakeDriverIT.java | 431 +++++++-------- .../client/jdbc/SnowflakeDriverLatestIT.java | 276 ++++++---- .../client/jdbc/SnowflakeDriverTest.java | 28 +- ...flakeGcsClientHandleExceptionLatestIT.java | 154 +++--- ...SnowflakeResultSetSerializableArrowIT.java | 12 - .../SnowflakeResultSetSerializableIT.java | 202 +++---- ...wflakeS3ClientHandleExceptionLatestIT.java | 173 +++--- .../jdbc/SnowflakeSerializableTest.java | 10 +- .../SnowflakeTimestampWithTimezoneTest.java | 87 ++- .../client/jdbc/SnowflakeTypeTest.java | 11 +- .../client/jdbc/SnowflakeUtilTest.java | 16 +- .../SqlFeatureNotSupportedTelemetryTest.java | 4 +- .../client/jdbc/StatementAlreadyClosedIT.java | 12 +- .../client/jdbc/StatementArrowIT.java | 6 +- .../jdbc/StatementFeatureNotSupportedIT.java | 8 +- .../snowflake/client/jdbc/StatementIT.java | 39 +- .../client/jdbc/StatementLargeUpdateIT.java | 10 +- .../client/jdbc/StatementLatestIT.java | 33 +- .../client/jdbc/StatementNoOpLatestIT.java | 12 +- .../net/snowflake/client/jdbc/StreamIT.java | 21 +- .../snowflake/client/jdbc/StreamLatestIT.java | 43 +- .../storage/CloudStorageClientLatestIT.java | 14 +- .../cloud/storage/EncryptionProviderTest.java | 8 +- .../storage/GcmEncryptionProviderTest.java | 46 +- .../storage/SnowflakeAzureClientLatestIT.java | 21 +- .../storage/SnowflakeAzureClientTest.java | 4 +- .../storage/SnowflakeS3ClientLatestIT.java | 29 +- .../cloud/storage/SnowflakeS3ClientTest.java | 4 +- .../StageInfoGcsCustomEndpointTest.java | 78 +-- .../diagnostic/DiagnosticContextLatestIT.java | 50 +- .../diagnostic/SnowflakeEndpointTest.java | 8 +- .../ResultSetStructuredTypesLatestIT.java | 512 ++++++++++-------- ...ypesGetStringArrowJsonCompatibilityIT.java | 228 ++++---- .../StructuredTypesGetStringBaseIT.java | 13 +- .../client/jdbc/telemetry/TelemetryIT.java | 48 +- .../client/jdbc/telemetry/TelemetryTest.java | 4 +- .../jdbc/telemetryOOB/TelemetryServiceIT.java | 63 ++- .../telemetryOOB/TelemetryServiceTest.java | 10 +- .../loader/FlatfileReadMultithreadIT.java | 16 +- .../snowflake/client/loader/LoaderBase.java | 8 +- .../net/snowflake/client/loader/LoaderIT.java | 16 +- .../client/loader/LoaderLatestIT.java | 12 +- .../client/loader/LoaderMultipleBatchIT.java | 10 +- .../client/loader/LoaderTimestampIT.java | 10 +- .../snowflake/client/loader/OnErrorTest.java | 2 +- .../client/log/AbstractLoggerIT.java | 30 +- .../client/log/JDK14JCLWrapperLatestIT.java | 22 +- .../client/log/JDK14LoggerLatestIT.java | 22 +- .../snowflake/client/log/JDK14LoggerTest.java | 8 +- .../log/JDK14LoggerWithClientLatestIT.java | 90 +-- .../snowflake/client/log/SFFormatterTest.java | 12 +- .../snowflake/client/log/SFLogLevelTest.java | 4 +- .../client/log/SFLoggerFactoryTest.java | 4 +- .../client/log/SFToJavaLogMapperTest.java | 14 +- .../client/log/SLF4JJJCLWrapperLatestIT.java | 22 +- .../client/log/SLF4JLoggerLatestIT.java | 23 +- .../ConnectionPoolingDataSourceIT.java | 10 +- ...ogicalConnectionAlreadyClosedLatestIT.java | 8 +- ...ConnectionFeatureNotSupportedLatestIT.java | 8 +- .../pooling/LogicalConnectionLatestIT.java | 20 +- .../client/providers/BooleanProvider.java | 16 + .../client/providers/ProvidersUtil.java | 37 ++ .../providers/ResultFormatProvider.java | 20 + .../client/providers/ScaleProvider.java | 20 + .../providers/SimpleResultFormatProvider.java | 27 + .../providers/SnowflakeArgumentsProvider.java | 19 + .../client/providers/TimezoneProvider.java | 36 ++ .../client/suites/ArrowTestSuite.java | 11 + .../client/suites/BaseTestSuite.java | 23 + .../suites/ConnectionOldDriverTestSuite.java | 10 + .../client/suites/ConnectionTestSuite.java | 11 + .../client/suites/CoreOldDriverTestSuite.java | 10 + .../client/suites/CoreTestSuite.java | 11 + .../suites/DiagnosticOldDriverTestSuite.java | 10 + .../client/suites/DiagnosticTestSuite.java | 11 + .../suites/LoaderOldDriverTestSuite.java | 10 + .../client/suites/LoaderTestSuite.java | 11 + .../client/suites/OldDriverTestSuite.java | 23 + .../suites/OthersOldDriverTestSuite.java | 10 + .../client/suites/OthersTestSuite.java | 11 + .../suites/ResultSetOldDriverTestSuite.java | 10 + .../client/suites/ResultSetTestSuite.java | 11 + .../suites/StatementOldDriverTestSuite.java | 10 + .../client/suites/StatementTestSuite.java | 11 + .../client/suites/UnitOldDriverTestSuite.java | 19 + .../client/suites/UnitTestSuite.java | 22 + .../client/util/SecretDetectorTest.java | 4 +- .../snowflake/client/util/StopwatchTest.java | 12 +- 300 files changed, 6261 insertions(+), 5413 deletions(-) delete mode 100644 FIPS/src/test/java/net/snowflake/client/ConditionalIgnoreRule.java create mode 100644 FIPS/src/test/java/net/snowflake/client/DontRunOnGCP.java create mode 100644 FIPS/src/test/java/net/snowflake/client/DontRunOnGithubActions.java delete mode 100644 FIPS/src/test/java/net/snowflake/client/RunningOnGCP.java delete mode 100644 FIPS/src/test/java/net/snowflake/client/RunningOnGithubActions.java delete mode 100644 FIPS/src/test/java/net/snowflake/client/RunningOnWinMac.java create mode 100644 FIPS/src/test/java/net/snowflake/client/category/FipsTestSuite.java delete mode 100644 FIPS/src/test/java/net/snowflake/client/category/TestCategoryFips.java create mode 100644 src/test/java/net/snowflake/client/AssumptionUtils.java delete mode 100644 src/test/java/net/snowflake/client/ConditionalIgnoreRule.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnAWS.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnAzure.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnGCP.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnGithubActionsMac.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnJava21.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnJava8.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnLinux.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnLinuxMac.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnTestaccount.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnWin.java delete mode 100644 src/test/java/net/snowflake/client/RunningNotOnWinMac.java delete mode 100644 src/test/java/net/snowflake/client/RunningOnGithubAction.java delete mode 100644 src/test/java/net/snowflake/client/RunningOnTestaccount.java delete mode 100644 src/test/java/net/snowflake/client/RunningOnWin.java delete mode 100644 src/test/java/net/snowflake/client/SkipOnThinJar.java create mode 100644 src/test/java/net/snowflake/client/annotations/DontRunOnGithubActions.java create mode 100644 src/test/java/net/snowflake/client/annotations/DontRunOnJava21.java create mode 100644 src/test/java/net/snowflake/client/annotations/DontRunOnJava8.java create mode 100644 src/test/java/net/snowflake/client/annotations/DontRunOnTestaccount.java create mode 100644 src/test/java/net/snowflake/client/annotations/DontRunOnThinJar.java create mode 100644 src/test/java/net/snowflake/client/annotations/DontRunOnWindows.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnAWS.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnAzure.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnGCP.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnGithubActionsNotMac.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnLinux.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnLinuxOrMac.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnMac.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnTestaccountNotOnGithubActions.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnWindows.java create mode 100644 src/test/java/net/snowflake/client/annotations/RunOnWindowsOrMac.java delete mode 100644 src/test/java/net/snowflake/client/category/TestCategoryArrow.java delete mode 100644 src/test/java/net/snowflake/client/category/TestCategoryConnection.java delete mode 100644 src/test/java/net/snowflake/client/category/TestCategoryCore.java delete mode 100644 src/test/java/net/snowflake/client/category/TestCategoryDiagnostic.java delete mode 100644 src/test/java/net/snowflake/client/category/TestCategoryLoader.java delete mode 100644 src/test/java/net/snowflake/client/category/TestCategoryOthers.java delete mode 100644 src/test/java/net/snowflake/client/category/TestCategoryResultSet.java delete mode 100644 src/test/java/net/snowflake/client/category/TestCategoryStatement.java create mode 100644 src/test/java/net/snowflake/client/category/TestTags.java create mode 100644 src/test/java/net/snowflake/client/jdbc/CallableStatementITBase.java delete mode 100644 src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow1IT.java delete mode 100644 src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow1LatestIT.java delete mode 100644 src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow2IT.java delete mode 100644 src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow2LatestIT.java delete mode 100644 src/test/java/net/snowflake/client/jdbc/ResultSetArrowIT.java delete mode 100644 src/test/java/net/snowflake/client/jdbc/ResultSetArrowLatestIT.java delete mode 100644 src/test/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableArrowIT.java create mode 100644 src/test/java/net/snowflake/client/providers/BooleanProvider.java create mode 100644 src/test/java/net/snowflake/client/providers/ProvidersUtil.java create mode 100644 src/test/java/net/snowflake/client/providers/ResultFormatProvider.java create mode 100644 src/test/java/net/snowflake/client/providers/ScaleProvider.java create mode 100644 src/test/java/net/snowflake/client/providers/SimpleResultFormatProvider.java create mode 100644 src/test/java/net/snowflake/client/providers/SnowflakeArgumentsProvider.java create mode 100644 src/test/java/net/snowflake/client/providers/TimezoneProvider.java create mode 100644 src/test/java/net/snowflake/client/suites/ArrowTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/BaseTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/ConnectionOldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/ConnectionTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/CoreOldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/CoreTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/DiagnosticOldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/DiagnosticTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/LoaderOldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/LoaderTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/OldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/OthersOldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/OthersTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/ResultSetOldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/ResultSetTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/StatementOldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/StatementTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/UnitOldDriverTestSuite.java create mode 100644 src/test/java/net/snowflake/client/suites/UnitTestSuite.java diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index ef331f720..c93f081f0 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -38,13 +38,16 @@ jobs: test-windows: needs: build - name: ${{ matrix.runConfig.cloud }} Windows java ${{ matrix.runConfig.javaVersion }} JDBC${{ matrix.additionalMavenProfile }} ${{ matrix.category }} + name: ${{ matrix.runConfig.cloud }} Windows java ${{ matrix.runConfig.javaVersion }} JDBC${{ matrix.additionalMavenProfile }} ${{ matrix.category.name }} runs-on: windows-latest strategy: fail-fast: false matrix: runConfig: [ {cloud: 'AWS', javaVersion: '8'}, {cloud: 'GCP', javaVersion: '11'}, {cloud: 'AZURE', javaVersion: '17'}, {cloud: 'AWS', javaVersion: '21'}] - category: ['TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader', 'TestCategoryOthers', 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic', 'TestCategoryFips'] + category: [{suites: 'ResultSetTestSuite,StatementTestSuite,LoaderTestSuite', name: 'TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader'}, + {suites: 'OthersTestSuite', name: 'TestCategoryOthers'}, + {suites: 'ArrowTestSuite,ConnectionTestSuite,CoreTestSuite,DiagnosticTestSuite', name: 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic'}, + {suites: 'FipsTestSuite', name: "TestCategoryFips"}] additionalMavenProfile: [''] steps: - uses: actions/checkout@v4 @@ -62,19 +65,22 @@ jobs: env: PARAMETERS_SECRET: ${{ secrets.PARAMETERS_SECRET }} CLOUD_PROVIDER: ${{ matrix.runConfig.cloud }} - JDBC_TEST_CATEGORY: ${{ matrix.category }} + JDBC_TEST_SUITES: ${{ matrix.category.suites }} ADDITIONAL_MAVEN_PROFILE: ${{ matrix.additionalMavenProfile }} run: ci\\test_windows.bat test-mac: needs: build - name: ${{ matrix.runConfig.cloud }} Mac java ${{ matrix.runConfig.javaVersion }} JDBC${{ matrix.additionalMavenProfile }} ${{ matrix.category }} + name: ${{ matrix.runConfig.cloud }} Mac java ${{ matrix.runConfig.javaVersion }} JDBC${{ matrix.additionalMavenProfile }} ${{ matrix.category.name }} runs-on: macos-13 strategy: fail-fast: false matrix: runConfig: [ {cloud: 'AWS', javaVersion: '8'}, {cloud: 'GCP', javaVersion: '11'}, {cloud: 'AZURE', javaVersion: '17'}, {cloud: 'AWS', javaVersion: '21'}] - category: ['TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader', 'TestCategoryOthers', 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic', 'TestCategoryFips'] + category: [{suites: 'ResultSetTestSuite,StatementTestSuite,LoaderTestSuite', name: 'TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader'}, + {suites: 'OthersTestSuite', name: 'TestCategoryOthers'}, + {suites: 'ArrowTestSuite,ConnectionTestSuite,CoreTestSuite,DiagnosticTestSuite', name: 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic'}, + {suites: 'FipsTestSuite', name: "TestCategoryFips"}] additionalMavenProfile: [''] steps: - uses: actions/checkout@v4 @@ -94,20 +100,23 @@ jobs: env: PARAMETERS_SECRET: ${{ secrets.PARAMETERS_SECRET }} CLOUD_PROVIDER: ${{ matrix.runConfig.cloud }} - JDBC_TEST_CATEGORY: ${{ matrix.category }} + JDBC_TEST_SUITES: ${{ matrix.category.suites }} ADDITIONAL_MAVEN_PROFILE: ${{ matrix.additionalMavenProfile }} run: /usr/local/bin/bash ./ci/test_mac.sh test-linux: needs: build - name: ${{ matrix.cloud }} Linux java on ${{ matrix.image }} JDBC${{ matrix.additionalMavenProfile }} ${{ matrix.category }} + name: ${{ matrix.cloud }} Linux java on ${{ matrix.image }} JDBC${{ matrix.additionalMavenProfile }} ${{ matrix.category.name }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: image: [ 'jdbc-centos7-openjdk8', 'jdbc-centos7-openjdk11', 'jdbc-centos7-openjdk17', 'jdbc-centos7-openjdk21' ] cloud: [ 'AWS', 'AZURE', 'GCP' ] - category: ['TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader', 'TestCategoryOthers', 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic', 'TestCategoryFips'] + category: [{suites: 'ResultSetTestSuite,StatementTestSuite,LoaderTestSuite', name: 'TestCategoryResultSet,TestCategoryStatement,TestCategoryLoader'}, + {suites: 'OthersTestSuite', name: 'TestCategoryOthers'}, + {suites: 'ArrowTestSuite,ConnectionTestSuite,CoreTestSuite,DiagnosticTestSuite', name: 'TestCategoryArrow,TestCategoryConnection,TestCategoryCore,TestCategoryDiagnostic'}, + {suites: 'FipsTestSuite', name: "TestCategoryFips"}] additionalMavenProfile: ['', '-Dthin-jar'] steps: - uses: actions/checkout@v4 @@ -117,19 +126,21 @@ jobs: PARAMETERS_SECRET: ${{ secrets.PARAMETERS_SECRET }} CLOUD_PROVIDER: ${{ matrix.cloud }} TARGET_DOCKER_TEST_IMAGE: ${{ matrix.image }} - JDBC_TEST_CATEGORY: ${{ matrix.category }} + JDBC_TEST_SUITES: ${{ matrix.category.suites }} ADDITIONAL_MAVEN_PROFILE: ${{ matrix.additionalMavenProfile }} run: ./ci/test.sh test-linux-old-driver: - name: Old JDBC ${{ matrix.category }} on ${{ matrix.image }} + name: Old JDBC ${{ matrix.category.name }} on ${{ matrix.image }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: image: [ 'jdbc-centos7-openjdk8' ] cloud: [ 'AWS' ] - category: ['TestCategoryOthers', 'TestCategoryConnection,TestCategoryStatement', 'TestCategoryCore,TestCategoryLoader,TestCategoryResultSet'] + category: [{suites: 'OthersOldDriverTestSuite', name: 'TestCategoryOthers'}, + {suites: 'ConnectionOldDriverTestSuite,StatementOldDriverTestSuite', name: 'TestCategoryConnection,TestCategoryStatement'}, + {suites: 'LoaderOldDriverTestSuite,ResultSetOldDriverTestSuite', name: 'TestCategoryLoader,TestCategoryResultSet'}] is_old_driver: ['true'] steps: - uses: actions/checkout@v4 @@ -139,6 +150,6 @@ jobs: PARAMETERS_SECRET: ${{ secrets.PARAMETERS_SECRET }} CLOUD_PROVIDER: ${{ matrix.cloud }} TARGET_DOCKER_TEST_IMAGE: ${{ matrix.image }} - JDBC_TEST_CATEGORY: ${{ matrix.category }} + JDBC_TEST_SUITES: ${{ matrix.category.suites }} is_old_driver: ${{ matrix.is_old_driver }} run: ./ci/test.sh diff --git a/FIPS/pom.xml b/FIPS/pom.xml index b8efd8c05..78e83700d 100644 --- a/FIPS/pom.xml +++ b/FIPS/pom.xml @@ -725,6 +725,13 @@ maven-failsafe-plugin + + + org.apache.maven.surefire + surefire-junit-platform + ${version.plugin.surefire} + + ${version.plugin.failsafe} @@ -769,6 +776,13 @@ org.apache.maven.plugins maven-failsafe-plugin + + + org.apache.maven.surefire + surefire-junit-platform + ${version.plugin.surefire} + + ${version.plugin.failsafe} diff --git a/FIPS/src/test/java/net/snowflake/client/AbstractDriverIT.java b/FIPS/src/test/java/net/snowflake/client/AbstractDriverIT.java index 05c389208..360a1fcbb 100644 --- a/FIPS/src/test/java/net/snowflake/client/AbstractDriverIT.java +++ b/FIPS/src/test/java/net/snowflake/client/AbstractDriverIT.java @@ -21,12 +21,10 @@ import java.util.TimeZone; import java.util.logging.Level; import java.util.logging.Logger; -import org.junit.Rule; /** Base test class with common constants, data structures and methods */ public class AbstractDriverIT { // This is required to use ConditionalIgnore annotation. - @Rule public ConditionalIgnoreRule rule = new ConditionalIgnoreRule(); public static final String DRIVER_CLASS = "net.snowflake.client.jdbc.SnowflakeDriver"; public static final String DRIVER_CLASS_COM = "com.snowflake.client.jdbc.SnowflakeDriver"; diff --git a/FIPS/src/test/java/net/snowflake/client/ConditionalIgnoreRule.java b/FIPS/src/test/java/net/snowflake/client/ConditionalIgnoreRule.java deleted file mode 100644 index fe20883db..000000000 --- a/FIPS/src/test/java/net/snowflake/client/ConditionalIgnoreRule.java +++ /dev/null @@ -1,125 +0,0 @@ -package net.snowflake.client; - -/* - * Created by hyu on 1/22/18. - */ - -/* -Copyright (c) 2013,2014 Rüdiger Herrmann -All rights reserved. This program and the accompanying materials -are made available under the terms of the Eclipse Public License v1.0 -which accompanies this distribution, and is available at -http://www.eclipse.org/legal/epl-v10.html - -Contributors: -Rüdiger Herrmann - initial API and implementation -Matt Morrissette - allow to use non-static inner IgnoreConditions -*/ - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.Modifier; -import org.junit.Assume; -import org.junit.rules.MethodRule; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; - -public class ConditionalIgnoreRule implements MethodRule { - - public interface IgnoreCondition { - boolean isSatisfied(); - } - - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.METHOD}) - public @interface ConditionalIgnore { - Class condition(); - } - - @Override - public Statement apply(Statement base, FrameworkMethod method, Object target) { - Statement result = base; - if (hasConditionalIgnoreAnnotation(method)) { - IgnoreCondition condition = getIgnoreCondition(target, method); - if (condition.isSatisfied()) { - result = new IgnoreStatement(condition); - } - } - return result; - } - - private static boolean hasConditionalIgnoreAnnotation(FrameworkMethod method) { - return method.getAnnotation(ConditionalIgnore.class) != null; - } - - private static IgnoreCondition getIgnoreCondition(Object target, FrameworkMethod method) { - ConditionalIgnore annotation = method.getAnnotation(ConditionalIgnore.class); - return new IgnoreConditionCreator(target, annotation).create(); - } - - private static class IgnoreConditionCreator { - private final Object target; - private final Class conditionType; - - IgnoreConditionCreator(Object target, ConditionalIgnore annotation) { - this.target = target; - this.conditionType = annotation.condition(); - } - - IgnoreCondition create() { - checkConditionType(); - try { - return createCondition(); - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private IgnoreCondition createCondition() throws Exception { - IgnoreCondition result; - if (isConditionTypeStandalone()) { - result = conditionType.newInstance(); - } else { - result = conditionType.getDeclaredConstructor(target.getClass()).newInstance(target); - } - return result; - } - - private void checkConditionType() { - if (!isConditionTypeStandalone() && !isConditionTypeDeclaredInTarget()) { - String msg = - "Conditional class '%s' is a member class " - + "but was not declared inside the test case using it.\n" - + "Either make this class a static class, " - + "standalone class (by declaring it in it's own file) " - + "or move it inside the test case using it"; - throw new IllegalArgumentException(String.format(msg, conditionType.getName())); - } - } - - private boolean isConditionTypeStandalone() { - return !conditionType.isMemberClass() || Modifier.isStatic(conditionType.getModifiers()); - } - - private boolean isConditionTypeDeclaredInTarget() { - return target.getClass().isAssignableFrom(conditionType.getDeclaringClass()); - } - } - - private static class IgnoreStatement extends Statement { - private final IgnoreCondition condition; - - IgnoreStatement(IgnoreCondition condition) { - this.condition = condition; - } - - @Override - public void evaluate() { - Assume.assumeTrue("Ignored by " + condition.getClass().getSimpleName(), false); - } - } -} diff --git a/FIPS/src/test/java/net/snowflake/client/DontRunOnGCP.java b/FIPS/src/test/java/net/snowflake/client/DontRunOnGCP.java new file mode 100644 index 000000000..ccdf83206 --- /dev/null +++ b/FIPS/src/test/java/net/snowflake/client/DontRunOnGCP.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledIfEnvironmentVariable(named = "CLOUD_PROVIDER", matches = "(?i)GCP(?-i)") +public @interface DontRunOnGCP {} \ No newline at end of file diff --git a/FIPS/src/test/java/net/snowflake/client/DontRunOnGithubActions.java b/FIPS/src/test/java/net/snowflake/client/DontRunOnGithubActions.java new file mode 100644 index 000000000..98232e097 --- /dev/null +++ b/FIPS/src/test/java/net/snowflake/client/DontRunOnGithubActions.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledIfEnvironmentVariable(named = "GITHUB_ACTIONS", matches = ".*") +public @interface DontRunOnGithubActions {} diff --git a/FIPS/src/test/java/net/snowflake/client/RunningOnGCP.java b/FIPS/src/test/java/net/snowflake/client/RunningOnGCP.java deleted file mode 100644 index c902dc5f9..000000000 --- a/FIPS/src/test/java/net/snowflake/client/RunningOnGCP.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2012-2024 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client; - -/** Run tests only on specified cloud provider or ignore */ -public class RunningOnGCP implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - String cloudProvider = TestUtil.systemGetEnv("CLOUD_PROVIDER"); - return cloudProvider != null && cloudProvider.equalsIgnoreCase("GCP"); - } -} diff --git a/FIPS/src/test/java/net/snowflake/client/RunningOnGithubActions.java b/FIPS/src/test/java/net/snowflake/client/RunningOnGithubActions.java deleted file mode 100644 index d717b65dc..000000000 --- a/FIPS/src/test/java/net/snowflake/client/RunningOnGithubActions.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2012-2019 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client; - -/** Run tests on CI */ -public class RunningOnGithubActions implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return TestUtil.systemGetEnv("GITHUB_ACTIONS") != null; - } -} diff --git a/FIPS/src/test/java/net/snowflake/client/RunningOnWinMac.java b/FIPS/src/test/java/net/snowflake/client/RunningOnWinMac.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/FIPS/src/test/java/net/snowflake/client/TestUtil.java b/FIPS/src/test/java/net/snowflake/client/TestUtil.java index 703d59953..8bec5498f 100644 --- a/FIPS/src/test/java/net/snowflake/client/TestUtil.java +++ b/FIPS/src/test/java/net/snowflake/client/TestUtil.java @@ -9,7 +9,7 @@ import net.snowflake.client.core.SFException; import net.snowflake.client.log.SFLogger; import net.snowflake.client.log.SFLoggerFactory; -import org.junit.Assert; +import org.junit.jupiter.api.Assertions; public class TestUtil { private static final SFLogger logger = SFLoggerFactory.getLogger(TestUtil.class); @@ -22,7 +22,7 @@ public class TestUtil { public static void assertSFException(int errorCode, TestRunInterface testCode) { try { testCode.run(); - Assert.fail(); + Assertions.fail(); } catch (SFException e) { assertThat(e.getVendorCode(), is(errorCode)); } diff --git a/FIPS/src/test/java/net/snowflake/client/category/FipsTestSuite.java b/FIPS/src/test/java/net/snowflake/client/category/FipsTestSuite.java new file mode 100644 index 000000000..d61ce2a83 --- /dev/null +++ b/FIPS/src/test/java/net/snowflake/client/category/FipsTestSuite.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.category; + +import org.junit.platform.suite.api.IncludeTags; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.platform.suite.api.ExcludePackages; +import org.junit.platform.suite.api.IncludeClassNamePatterns; +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.api.SuiteDisplayName; + +@Suite +@SelectPackages("net.snowflake.client") +@ExcludePackages("net.snowflake.client.suites") +@IncludeClassNamePatterns(".+") +public class FipsTestSuite { +} diff --git a/FIPS/src/test/java/net/snowflake/client/category/TestCategoryFips.java b/FIPS/src/test/java/net/snowflake/client/category/TestCategoryFips.java deleted file mode 100644 index 06ae9faad..000000000 --- a/FIPS/src/test/java/net/snowflake/client/category/TestCategoryFips.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.snowflake.client.category; - -public interface TestCategoryFips {} diff --git a/FIPS/src/test/java/net/snowflake/client/jdbc/ConnectionFipsIT.java b/FIPS/src/test/java/net/snowflake/client/jdbc/ConnectionFipsIT.java index c1509a6a8..0204e9a5d 100644 --- a/FIPS/src/test/java/net/snowflake/client/jdbc/ConnectionFipsIT.java +++ b/FIPS/src/test/java/net/snowflake/client/jdbc/ConnectionFipsIT.java @@ -3,7 +3,7 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import java.net.URL; import java.nio.file.Files; @@ -20,21 +20,20 @@ import java.util.Properties; import javax.net.ssl.HttpsURLConnection; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGCP; -import net.snowflake.client.RunningOnGithubActions; -import net.snowflake.client.category.TestCategoryFips; +import net.snowflake.client.DontRunOnGCP; +import net.snowflake.client.DontRunOnGithubActions; import net.snowflake.client.core.SecurityUtil; import org.apache.commons.codec.binary.Base64; import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.fips.FipsStatus; import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryFips.class) + +@Tag("fips") public class ConnectionFipsIT extends AbstractDriverIT { private static final String JCE_PROVIDER_BOUNCY_CASTLE_FIPS = "BCFIPS"; private static final String JCE_PROVIDER_SUN_JCE = "SunJCE"; @@ -106,7 +105,7 @@ public class ConnectionFipsIT extends AbstractDriverIT { private static int JCE_PROVIDER_SUN_JCE_PROVIDER_POSITION; private static int JCE_PROVIDER_SUN_RSA_SIGN_PROVIDER_POSITION; - @BeforeClass + @BeforeAll public static void setup() throws Exception { System.setProperty("javax.net.debug", "ssl"); // get keystore types for BouncyCastle libraries @@ -166,7 +165,7 @@ public static void setup() throws Exception { // connectToGoogle(); } - @AfterClass + @AfterAll public static void teardown() throws Exception { // Remove BouncyCastle FIPS Provider Security.removeProvider(JCE_PROVIDER_BOUNCY_CASTLE_FIPS); @@ -227,7 +226,7 @@ public void connectWithFips() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubActions.class) + @DontRunOnGithubActions public void connectWithFipsKeyPair() throws Exception { Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); @@ -256,7 +255,7 @@ public void connectWithFipsKeyPair() throws Exception { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubActions.class) + @DontRunOnGithubActions public void testConnectUsingKeyPair() throws Exception { Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); @@ -295,7 +294,7 @@ public void testConnectUsingKeyPair() throws Exception { * Currently ignored execution on GCP due to exception thrown "SSlException Could not generate XDH keypair" */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGCP.class) + @DontRunOnGCP public void connectWithFipsAndQuery() throws SQLException { try (Connection con = getConnection()) { Statement statement = con.createStatement(); @@ -329,7 +328,7 @@ public void connectWithFipsAndPut() throws Exception { /** Added in > 3.15.1 */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubActions.class) + @DontRunOnGithubActions public void connectWithFipsKeyPairWithBouncyCastle() throws Exception { System.setProperty(SecurityUtil.ENABLE_BOUNCYCASTLE_PROVIDER_JVM, "true"); connectWithFipsKeyPair(); @@ -337,7 +336,7 @@ public void connectWithFipsKeyPairWithBouncyCastle() throws Exception { /** Added in > 3.15.1 */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubActions.class) + @DontRunOnGithubActions public void testConnectUsingKeyPairWithBouncyCastle() throws Exception { System.setProperty(SecurityUtil.ENABLE_BOUNCYCASTLE_PROVIDER_JVM, "true"); testConnectUsingKeyPair(); diff --git a/TestOnly/pom.xml b/TestOnly/pom.xml index e03f87ef1..509cb8925 100644 --- a/TestOnly/pom.xml +++ b/TestOnly/pom.xml @@ -18,9 +18,10 @@ 0.8.4 true 5.13.0 + 5.11.1 + 3.5.1 3.5.6 net.snowflake.client.jdbc.internal - net.snowflake.client.category.AllTestCategory @@ -38,13 +39,61 @@ org.apache.maven.plugins maven-failsafe-plugin 3.0.0-M1 + test - junit - junit - 4.13.1 - jar + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.platform + junit-platform-suite + 1.11.1 + test + + + org.junit.platform + junit-platform-engine + 1.11.1 + test + + + org.junit.platform + junit-platform-runner + 1.11.1 + test + + + org.junit.platform + junit-platform-suite-api + 1.11.1 + test + + + org.junit.platform + junit-platform-suite-engine + 1.11.1 + test + + + org.junit.platform + junit-platform-launcher + 1.11.1 test @@ -371,7 +420,26 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + + + org.apache.maven.surefire + surefire-junit-platform + ${surefire.version} + + + ${surefire.version} + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.apache.maven.surefire + surefire-junit-platform + ${surefire.version} + + + ${surefire.version} @@ -387,35 +455,40 @@ org.apache.maven.plugins - maven-failsafe-plugin + maven-surefire-plugin - ${testCategory} + false + + + test + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + verify + + DefaultIT integration-test - - **/DellBoomiCloudIT.java - - - net.snowflake.client.log.JDK14Logger - - - ${basedir}/../src/test/resources/logging.properties - + net.snowflake.client.log.JDK14Logger + ${basedir}/src/test/resources/logging.properties + ${integrationTestSuites} - - - verify - - diff --git a/ci/container/test_component.sh b/ci/container/test_component.sh index da245a627..65efed88d 100755 --- a/ci/container/test_component.sh +++ b/ci/container/test_component.sh @@ -68,9 +68,6 @@ echo "[INFO] Running Hang Web Server" kill -9 $(ps -ewf | grep hang_webserver | grep -v grep | awk '{print $2}') || true python3 $THIS_DIR/hang_webserver.py 12345& -IFS=',' -read -ra CATEGORY <<< "$JDBC_TEST_CATEGORY" - # Avoid connection timeouts export MAVEN_OPTS="$MAVEN_OPTS -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120" @@ -79,41 +76,39 @@ cd $SOURCE_ROOT # Avoid connection timeout on plugin dependency fetch or fail-fast when dependency cannot be fetched $MVNW_EXE --batch-mode --show-version dependency:go-offline -for c in "${CATEGORY[@]}"; do - c=$(echo $c | sed 's/ *$//g') - if [[ "$is_old_driver" == "true" ]]; then - pushd TestOnly >& /dev/null - JDBC_VERSION=$($MVNW_EXE org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version --batch-mode | grep -v "[INFO]") - echo "[INFO] Run JDBC $JDBC_VERSION tests" - $MVNW_EXE -DjenkinsIT \ - -Djava.io.tmpdir=$WORKSPACE \ - -Djacoco.skip.instrument=false \ - -DtestCategory=net.snowflake.client.category.$c \ - -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ - verify \ - --batch-mode --show-version - popd >& /dev/null - elif [[ "$c" == "TestCategoryFips" ]]; then - pushd FIPS >& /dev/null - echo "[INFO] Run Fips tests" - $MVNW_EXE -DjenkinsIT \ - -Djava.io.tmpdir=$WORKSPACE \ - -Djacoco.skip.instrument=false \ - -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ - -Dnot-self-contained-jar \ - verify \ - --batch-mode --show-version - popd >& /dev/null - else - echo "[INFO] Run $c tests" +if [[ "$is_old_driver" == "true" ]]; then + pushd TestOnly >& /dev/null + JDBC_VERSION=$($MVNW_EXE org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version --batch-mode | grep -v "[INFO]") + echo "[INFO] Run JDBC $JDBC_VERSION tests" $MVNW_EXE -DjenkinsIT \ -Djava.io.tmpdir=$WORKSPACE \ -Djacoco.skip.instrument=false \ - -DtestCategory=net.snowflake.client.category.$c \ + -DintegrationTestSuites="$JDBC_TEST_SUITES" \ -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ - -Dnot-self-contained-jar $ADDITIONAL_MAVEN_PROFILE \ verify \ --batch-mode --show-version - fi -done + popd >& /dev/null +elif [[ "$JDBC_TEST_SUITES" == "FipsTestSuite" ]]; then + pushd FIPS >& /dev/null + echo "[INFO] Run Fips tests" + $MVNW_EXE -DjenkinsIT \ + -Djava.io.tmpdir=$WORKSPACE \ + -Djacoco.skip.instrument=false \ + -DintegrationTestSuites=FipsTestSuite \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ + -Dnot-self-contained-jar \ + verify \ + --batch-mode --show-version + popd >& /dev/null +else + echo "[INFO] Run $JDBC_TEST_SUITES tests" + $MVNW_EXE -DjenkinsIT \ + -Djava.io.tmpdir=$WORKSPACE \ + -Djacoco.skip.instrument=false \ + -DintegrationTestSuites="$JDBC_TEST_SUITES" \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn \ + -Dnot-self-contained-jar $ADDITIONAL_MAVEN_PROFILE \ + verify \ + --batch-mode --show-version +fi IFS=' ' diff --git a/ci/log_analyze_setup.sh b/ci/log_analyze_setup.sh index fd573d194..63303964e 100755 --- a/ci/log_analyze_setup.sh +++ b/ci/log_analyze_setup.sh @@ -36,7 +36,7 @@ LOG_PROPERTY_FILE=$(cd "$(dirname "${BASH_SOURCE[0]}")/.."; pwd)/src/test/resour export CLIENT_DRIVER_NAME=JDBC function setup_log_env() { - if ["$WORKSPACE" == "/mnt/workspace"]; then + if [[ "$WORKSPACE" == "/mnt/workspace" ]]; then CLIENT_LOG_DIR_PATH=$LOCAL_CLIENT_LOG_DIR_PATH_DOCKER CLIENT_LOG_FILE_PATH=$CLIENT_LOG_FILE_PATH_DOCKER CLIENT_KNOWN_SSM_FILE_PATH=$CLIENT_KNOWN_SSM_FILE_PATH_DOCKER @@ -53,7 +53,7 @@ function setup_log_env() { sed -i'' -e "s|^java.util.logging.FileHandler.pattern.*|java.util.logging.FileHandler.pattern = $CLIENT_LOG_FILE_PATH|" ${LOG_PROPERTY_FILE} if [[ ! -d ${CLIENT_LOG_DIR_PATH} ]]; then - echo "[INFO] create clien log directory $CLIENT_LOG_DIR_PATH" + echo "[INFO] create client log directory $CLIENT_LOG_DIR_PATH" mkdir -p ${CLIENT_LOG_DIR_PATH} fi diff --git a/ci/test.sh b/ci/test.sh index 03c66c502..125e91d1f 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -30,8 +30,8 @@ else exit 2 fi -if [[ -z "$JDBC_TEST_CATEGORY" ]]; then - echo "[ERROR] Set JDBC_TEST_CATEGORY to the JDBC test category." +if [[ -z "$JDBC_TEST_SUITES" ]]; then + echo "[ERROR] Set JDBC_TEST_SUITES to the JDBC test category." find $THIS_DIR/../src/test/java -type f -exec grep -E "^import net.snowflake.client.category" {} \; | sort | uniq | awk -F. '{print $NF}' | awk -F\; '{print $1}' exit 2 fi @@ -56,7 +56,7 @@ for name in "${!TARGET_TEST_IMAGES[@]}"; do -e RUNNER_TRACKING_ID \ -e JOB_NAME \ -e BUILD_NUMBER \ - -e JDBC_TEST_CATEGORY \ + -e JDBC_TEST_SUITES \ -e ADDITIONAL_MAVEN_PROFILE \ -e CLOUD_PROVIDER \ -e is_old_driver \ diff --git a/ci/test_windows.bat b/ci/test_windows.bat index 4a5a8ebe3..0234b105c 100644 --- a/ci/test_windows.bat +++ b/ci/test_windows.bat @@ -111,47 +111,45 @@ echo "MAVEN OPTIONS %MAVEN_OPTS%" REM Avoid connection timeout on plugin dependency fetch or fail-fast when dependency cannot be fetched cmd /c %MVNW_EXE% --batch-mode --show-version dependency:go-offline -echo list = "%JDBC_TEST_CATEGORY%" -for %%a in ("%JDBC_TEST_CATEGORY:,=" "%") do ( - echo "Current category to execute" %%a - if /i %%a=="TestCategoryFips" ( - pushd FIPS - echo "[INFO] Run Fips tests" - cmd /c %MVNW_EXE% -B -DjenkinsIT ^ - -Djava.io.tmpdir=%GITHUB_WORKSPACE% ^ - -Djacoco.skip.instrument=false ^ - -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn ^ - -Dnot-self-contained-jar ^ - verify ^ - --batch-mode --show-version > log.txt & type log.txt - echo "[INFO] Check for test execution status" - find /i /c "BUILD FAILURE" log.txt > NUL - set isfound=!errorlevel! - if !isfound! equ 0 ( - echo [ERROR] Failed run %%a test - exit /b 1 - ) else ( - echo [INFO] Success run %%a test - ) - popd ) else ( - echo "[INFO] Run %%a tests" - cmd /c %MVNW_EXE% -B -DjenkinsIT ^ - -Djava.io.tmpdir=%GITHUB_WORKSPACE% ^ - -Djacoco.skip.instrument=false ^ - -DtestCategory=net.snowflake.client.category.%%a ^ - -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn ^ - -Dnot-self-contained-jar %ADDITIONAL_MAVEN_PROFILE% ^ - verify ^ - --batch-mode --show-version > log.txt & type log.txt - echo "[INFO] Check for test execution status" - find /i /c "BUILD FAILURE" log.txt > NUL - set isfound=!errorlevel! - if !isfound! equ 0 ( - echo [ERROR] Failed run %%a test - exit /b 1 - ) else ( - echo [INFO] Success run %%a test - ) +if "%JDBC_TEST_SUITES%"=="FipsTestSuite" ( + pushd FIPS + echo "[INFO] Run Fips tests" + cmd /c %MVNW_EXE% -B -DjenkinsIT ^ + -Djava.io.tmpdir=%GITHUB_WORKSPACE% ^ + -Djacoco.skip.instrument=false ^ + -DintegrationTestSuites=FipsTestSuite ^ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn ^ + -Dnot-self-contained-jar ^ + verify ^ + --batch-mode --show-version > log.txt & type log.txt + echo "[INFO] Check for test execution status" + find /i /c "BUILD FAILURE" log.txt > NUL + set isfound=!errorlevel! + if !isfound! equ 0 ( + echo [ERROR] Failed run %%a test + exit /b 1 + ) else ( + echo [INFO] Success run %%a test + ) + popd +) else ( + echo "[INFO] Run %JDBC_TEST_SUITES% tests" + cmd /c %MVNW_EXE% -B -DjenkinsIT ^ + -Djava.io.tmpdir=%GITHUB_WORKSPACE% ^ + -Djacoco.skip.instrument=false ^ + -DintegrationTestSuites="%JDBC_TEST_SUITES%" ^ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn ^ + -Dnot-self-contained-jar %ADDITIONAL_MAVEN_PROFILE% ^ + verify ^ + --batch-mode --show-version > log.txt & type log.txt + echo "[INFO] Check for test execution status" + find /i /c "BUILD FAILURE" log.txt > NUL + set isfound=!errorlevel! + if !isfound! equ 0 ( + echo [ERROR] Failed run %%a test + exit /b 1 + ) else ( + echo [INFO] Success run %%a test ) ) diff --git a/parent-pom.xml b/parent-pom.xml index 1e5aac29c..b1742d64e 100644 --- a/parent-pom.xml +++ b/parent-pom.xml @@ -61,7 +61,9 @@ 5.13.0 2.8.1 2.4.9 - 4.13.2 + 4.13.2 + 5.11.1 + 1.11.1 1.15.3 1.3.6 2.2.0 @@ -78,7 +80,7 @@ net_snowflake_client_jdbc_internal 2.0.13 5.1.4 - net.snowflake.client.category.AllTestCategory + UnitTestSuite 2.4.1 1.9 3.6.3 @@ -91,7 +93,7 @@ 3.1.1 3.0.0-M3 3.1.0 - 3.0.0 + 3.5.1 2.19 3.0.1 3.1.1 @@ -103,7 +105,7 @@ 3.6.0 3.0.1 3.2.1 - 3.0.0 + 3.5.1 3.8.0 @@ -267,9 +269,69 @@ junit junit + ${junit4.version} + test + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params ${junit.version} test + + org.junit.platform + junit-platform-suite + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-engine + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-suite-api + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-suite-engine + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + test + org.apache.avro avro @@ -509,18 +571,6 @@ ${awaitility.version} test - - org.apache.maven.surefire - surefire-junit4 - ${version.plugin.surefire} - test - - - org.apache.maven.surefire - common-junit48 - ${version.plugin.surefire} - test - org.wiremock wiremock-standalone @@ -740,6 +790,46 @@ junit junit + + org.junit.jupiter + junit-jupiter + + + org.junit.jupiter + junit-jupiter-api + + + org.junit.jupiter + junit-jupiter-engine + + + org.junit.jupiter + junit-jupiter-params + + + org.junit.platform + junit-platform-suite + + + org.junit.platform + junit-platform-engine + + + org.junit.platform + junit-platform-runner + + + org.junit.platform + junit-platform-suite-api + + + org.junit.platform + junit-platform-suite-engine + + + org.junit.platform + junit-platform-launcher + org.apache.avro avro @@ -772,15 +862,6 @@ org.awaitility awaitility - - - org.apache.maven.surefire - surefire-junit4 - - - org.apache.maven.surefire - common-junit48 - org.wiremock wiremock-standalone diff --git a/pom.xml b/pom.xml index 0f34e2953..2cfb0425e 100644 --- a/pom.xml +++ b/pom.xml @@ -106,6 +106,13 @@ org.apache.maven.plugins maven-failsafe-plugin ${version.plugin.failsafe} + + + org.apache.maven.surefire + surefire-junit-platform + ${version.plugin.surefire} + + org.apache.maven.plugins @@ -146,6 +153,13 @@ org.apache.maven.plugins maven-surefire-plugin ${version.plugin.surefire} + + + org.apache.maven.surefire + surefire-junit-platform + ${version.plugin.surefire} + + org.codehaus.mojo @@ -1197,10 +1211,28 @@ org.apache.maven.plugins - maven-failsafe-plugin + maven-surefire-plugin - ${testCategory} + UnitTestSuite + + + org.apache.maven.surefire + surefire-junit-platform + ${version.plugin.surefire} + + + + + + test + + + + + + org.apache.maven.plugins + maven-failsafe-plugin @@ -1213,13 +1245,11 @@ integration-test - - **/DellBoomiCloudIT.java - net.snowflake.client.log.JDK14Logger ${basedir}/src/test/resources/logging.properties + ${integrationTestSuites} @@ -1338,27 +1368,24 @@ org.apache.maven.plugins maven-failsafe-plugin + + + **/*IT.java + + + + + org.apache.maven.surefire + surefire-junit-platform + ${version.plugin.surefire} + + verify - - ClientTelemetryIT - - integration-test - - - - **/ConnectionIT.java - **/SFTrustManagerIT.java - - - ${basedir}/src/test/resources/logback-test.xml - - - @@ -1376,21 +1403,24 @@ org.apache.maven.plugins maven-failsafe-plugin + + + **/*IT.java + + + + + org.apache.maven.surefire + surefire-junit-platform + ${version.plugin.surefire} + + verify - - DellBoomiIT - - integration-test - - - DellBoomiCloudIT.java - - @@ -1408,27 +1438,24 @@ org.apache.maven.plugins maven-failsafe-plugin + + + **/*IT.java + + + + + org.apache.maven.surefire + surefire-junit-platform + ${version.plugin.surefire} + + verify - - ClientTelemetryIT - - integration-test - - - - **/ConnectionIT.java - **/SFTrustManagerIT.java - - - ${basedir}/src/test/resources/logback-test.xml - - - diff --git a/src/test/java/com/snowflake/client/jdbc/SnowflakeDriverIT.java b/src/test/java/com/snowflake/client/jdbc/SnowflakeDriverIT.java index d2a7246f8..f4f226fa9 100644 --- a/src/test/java/com/snowflake/client/jdbc/SnowflakeDriverIT.java +++ b/src/test/java/com/snowflake/client/jdbc/SnowflakeDriverIT.java @@ -1,15 +1,15 @@ package com.snowflake.client.jdbc; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.SQLException; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.category.TestCategoryConnection; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class SnowflakeDriverIT extends AbstractDriverIT { @Test diff --git a/src/test/java/net/snowflake/client/AbstractDriverIT.java b/src/test/java/net/snowflake/client/AbstractDriverIT.java index 4a3acea23..3104ce7e9 100644 --- a/src/test/java/net/snowflake/client/AbstractDriverIT.java +++ b/src/test/java/net/snowflake/client/AbstractDriverIT.java @@ -24,12 +24,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nullable; -import org.junit.Rule; /** Base test class with common constants, data structures and methods */ public class AbstractDriverIT { - // This is required to use ConditionalIgnore annotation. - @Rule public ConditionalIgnoreRule rule = new ConditionalIgnoreRule(); public static final String DRIVER_CLASS = "net.snowflake.client.jdbc.SnowflakeDriver"; public static final String DRIVER_CLASS_COM = "com.snowflake.client.jdbc.SnowflakeDriver"; diff --git a/src/test/java/net/snowflake/client/AssumptionUtils.java b/src/test/java/net/snowflake/client/AssumptionUtils.java new file mode 100644 index 000000000..73ae13fbb --- /dev/null +++ b/src/test/java/net/snowflake/client/AssumptionUtils.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client; + +import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; +import static org.junit.jupiter.api.Assumptions.assumeFalse; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import net.snowflake.client.core.Constants; + +public class AssumptionUtils { + public static void assumeNotRunningOnGithubActionsMac() { + assumeFalse(isRunningOnGithubActions() && Constants.getOS() == Constants.OS.MAC); + } + + public static void assumeNotRunningOnJava8() { + assumeFalse(systemGetProperty("java.version").startsWith("1.8.0")); + } + + public static void assumeNotRunningOnJava21() { + assumeFalse(systemGetProperty("java.version").startsWith("21.")); + } + + public static void assumeRunningOnGithubActions() { + assumeTrue(isRunningOnGithubActions()); + } + + public static boolean isRunningOnGithubActions() { + return TestUtil.systemGetEnv("GITHUB_ACTIONS") != null; + } + + public static void assumeRunningOnLinuxMac() { + assumeTrue(Constants.getOS() == Constants.OS.LINUX || Constants.getOS() == Constants.OS.MAC); + } +} diff --git a/src/test/java/net/snowflake/client/ConditionalIgnoreRule.java b/src/test/java/net/snowflake/client/ConditionalIgnoreRule.java deleted file mode 100644 index fe20883db..000000000 --- a/src/test/java/net/snowflake/client/ConditionalIgnoreRule.java +++ /dev/null @@ -1,125 +0,0 @@ -package net.snowflake.client; - -/* - * Created by hyu on 1/22/18. - */ - -/* -Copyright (c) 2013,2014 Rüdiger Herrmann -All rights reserved. This program and the accompanying materials -are made available under the terms of the Eclipse Public License v1.0 -which accompanies this distribution, and is available at -http://www.eclipse.org/legal/epl-v10.html - -Contributors: -Rüdiger Herrmann - initial API and implementation -Matt Morrissette - allow to use non-static inner IgnoreConditions -*/ - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.Modifier; -import org.junit.Assume; -import org.junit.rules.MethodRule; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.Statement; - -public class ConditionalIgnoreRule implements MethodRule { - - public interface IgnoreCondition { - boolean isSatisfied(); - } - - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.METHOD}) - public @interface ConditionalIgnore { - Class condition(); - } - - @Override - public Statement apply(Statement base, FrameworkMethod method, Object target) { - Statement result = base; - if (hasConditionalIgnoreAnnotation(method)) { - IgnoreCondition condition = getIgnoreCondition(target, method); - if (condition.isSatisfied()) { - result = new IgnoreStatement(condition); - } - } - return result; - } - - private static boolean hasConditionalIgnoreAnnotation(FrameworkMethod method) { - return method.getAnnotation(ConditionalIgnore.class) != null; - } - - private static IgnoreCondition getIgnoreCondition(Object target, FrameworkMethod method) { - ConditionalIgnore annotation = method.getAnnotation(ConditionalIgnore.class); - return new IgnoreConditionCreator(target, annotation).create(); - } - - private static class IgnoreConditionCreator { - private final Object target; - private final Class conditionType; - - IgnoreConditionCreator(Object target, ConditionalIgnore annotation) { - this.target = target; - this.conditionType = annotation.condition(); - } - - IgnoreCondition create() { - checkConditionType(); - try { - return createCondition(); - } catch (RuntimeException re) { - throw re; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private IgnoreCondition createCondition() throws Exception { - IgnoreCondition result; - if (isConditionTypeStandalone()) { - result = conditionType.newInstance(); - } else { - result = conditionType.getDeclaredConstructor(target.getClass()).newInstance(target); - } - return result; - } - - private void checkConditionType() { - if (!isConditionTypeStandalone() && !isConditionTypeDeclaredInTarget()) { - String msg = - "Conditional class '%s' is a member class " - + "but was not declared inside the test case using it.\n" - + "Either make this class a static class, " - + "standalone class (by declaring it in it's own file) " - + "or move it inside the test case using it"; - throw new IllegalArgumentException(String.format(msg, conditionType.getName())); - } - } - - private boolean isConditionTypeStandalone() { - return !conditionType.isMemberClass() || Modifier.isStatic(conditionType.getModifiers()); - } - - private boolean isConditionTypeDeclaredInTarget() { - return target.getClass().isAssignableFrom(conditionType.getDeclaringClass()); - } - } - - private static class IgnoreStatement extends Statement { - private final IgnoreCondition condition; - - IgnoreStatement(IgnoreCondition condition) { - this.condition = condition; - } - - @Override - public void evaluate() { - Assume.assumeTrue("Ignored by " + condition.getClass().getSimpleName(), false); - } - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnAWS.java b/src/test/java/net/snowflake/client/RunningNotOnAWS.java deleted file mode 100644 index 70f54ab8f..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnAWS.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2012-2024 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client; - -/** Run tests only on specified cloud provider or ignore */ -public class RunningNotOnAWS implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - String cloudProvider = TestUtil.systemGetEnv("CLOUD_PROVIDER"); - return cloudProvider != null && !cloudProvider.equalsIgnoreCase("AWS"); - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnAzure.java b/src/test/java/net/snowflake/client/RunningNotOnAzure.java deleted file mode 100644 index e2a00966c..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnAzure.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2012-2024 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client; - -/** Run tests only on specified cloud provider or ignore */ -public class RunningNotOnAzure implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - String cloudProvider = TestUtil.systemGetEnv("CLOUD_PROVIDER"); - return cloudProvider != null && !cloudProvider.equalsIgnoreCase("Azure"); - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnGCP.java b/src/test/java/net/snowflake/client/RunningNotOnGCP.java deleted file mode 100644 index 7a5c7aafb..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnGCP.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2012-2024 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client; - -/** Run tests only on specified cloud provider or ignore */ -public class RunningNotOnGCP implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - String cloudProvider = TestUtil.systemGetEnv("CLOUD_PROVIDER"); - return cloudProvider != null && !cloudProvider.equalsIgnoreCase("GCP"); - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnGithubActionsMac.java b/src/test/java/net/snowflake/client/RunningNotOnGithubActionsMac.java deleted file mode 100644 index 9b872fc8b..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnGithubActionsMac.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2012-2019 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client; - -import net.snowflake.client.core.Constants; - -public class RunningNotOnGithubActionsMac implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return isRunningOnGithubActionsMac(); - } - - public static boolean isRunningOnGithubActionsMac() { - return TestUtil.systemGetEnv("GITHUB_ACTIONS") != null && Constants.getOS() == Constants.OS.MAC; - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnJava21.java b/src/test/java/net/snowflake/client/RunningNotOnJava21.java deleted file mode 100644 index 4e2e3e03c..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnJava21.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.snowflake.client; - -import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; - -public class RunningNotOnJava21 implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return isRunningOnJava21(); - } - - public static boolean isRunningOnJava21() { - return systemGetProperty("java.version").startsWith("21."); - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnJava8.java b/src/test/java/net/snowflake/client/RunningNotOnJava8.java deleted file mode 100644 index 8ee4b3e40..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnJava8.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.snowflake.client; - -import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; - -public class RunningNotOnJava8 implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return isRunningOnJava8(); - } - - public static boolean isRunningOnJava8() { - return systemGetProperty("java.version").startsWith("1.8.0"); - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnLinux.java b/src/test/java/net/snowflake/client/RunningNotOnLinux.java deleted file mode 100644 index 3cbaf1339..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnLinux.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.snowflake.client; - -import net.snowflake.client.core.Constants; - -public class RunningNotOnLinux implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return Constants.getOS() != Constants.OS.LINUX; - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnLinuxMac.java b/src/test/java/net/snowflake/client/RunningNotOnLinuxMac.java deleted file mode 100644 index a99eaa3b7..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnLinuxMac.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.snowflake.client; - -import net.snowflake.client.core.Constants; - -public class RunningNotOnLinuxMac implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return Constants.getOS() != Constants.OS.LINUX && Constants.getOS() != Constants.OS.MAC; - } - - public static boolean isNotRunningOnLinuxMac() { - return Constants.getOS() != Constants.OS.LINUX && Constants.getOS() != Constants.OS.MAC; - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnTestaccount.java b/src/test/java/net/snowflake/client/RunningNotOnTestaccount.java deleted file mode 100644 index 596f5ca55..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnTestaccount.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.snowflake.client; - -import static net.snowflake.client.RunningOnGithubAction.isRunningOnGithubAction; - -public class RunningNotOnTestaccount implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return (!("testaccount".equals(TestUtil.systemGetEnv("SNOWFLAKE_TEST_ACCOUNT"))) - || isRunningOnGithubAction()); - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnWin.java b/src/test/java/net/snowflake/client/RunningNotOnWin.java deleted file mode 100644 index ce5cdf7d1..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnWin.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.snowflake.client; - -import net.snowflake.client.core.Constants; - -public class RunningNotOnWin implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return Constants.getOS() != Constants.OS.WINDOWS; - } -} diff --git a/src/test/java/net/snowflake/client/RunningNotOnWinMac.java b/src/test/java/net/snowflake/client/RunningNotOnWinMac.java deleted file mode 100644 index 9d1c32bdc..000000000 --- a/src/test/java/net/snowflake/client/RunningNotOnWinMac.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.snowflake.client; - -import net.snowflake.client.core.Constants; - -public class RunningNotOnWinMac implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return Constants.getOS() != Constants.OS.MAC && Constants.getOS() != Constants.OS.WINDOWS; - } -} diff --git a/src/test/java/net/snowflake/client/RunningOnGithubAction.java b/src/test/java/net/snowflake/client/RunningOnGithubAction.java deleted file mode 100644 index 0326c4fca..000000000 --- a/src/test/java/net/snowflake/client/RunningOnGithubAction.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2012-2019 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client; - -/** Run tests on CI */ -public class RunningOnGithubAction implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return TestUtil.systemGetEnv("GITHUB_ACTIONS") != null; - } - - public static boolean isRunningOnGithubAction() { - return TestUtil.systemGetEnv("GITHUB_ACTIONS") != null; - } -} diff --git a/src/test/java/net/snowflake/client/RunningOnTestaccount.java b/src/test/java/net/snowflake/client/RunningOnTestaccount.java deleted file mode 100644 index 186496977..000000000 --- a/src/test/java/net/snowflake/client/RunningOnTestaccount.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.snowflake.client; - -public class RunningOnTestaccount implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return TestUtil.systemGetEnv("SNOWFLAKE_TEST_ACCOUNT").contains("testaccount"); - } -} diff --git a/src/test/java/net/snowflake/client/RunningOnWin.java b/src/test/java/net/snowflake/client/RunningOnWin.java deleted file mode 100644 index 025ab1e04..000000000 --- a/src/test/java/net/snowflake/client/RunningOnWin.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.snowflake.client; - -import net.snowflake.client.core.Constants; - -public class RunningOnWin implements ConditionalIgnoreRule.IgnoreCondition { - public boolean isSatisfied() { - return Constants.getOS() == Constants.OS.WINDOWS; - } -} diff --git a/src/test/java/net/snowflake/client/SkipOnThinJar.java b/src/test/java/net/snowflake/client/SkipOnThinJar.java deleted file mode 100644 index d02d104dd..000000000 --- a/src/test/java/net/snowflake/client/SkipOnThinJar.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2012-2024 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client; - -/** Skip tests on CI when thin jar is tested */ -public class SkipOnThinJar implements ConditionalIgnoreRule.IgnoreCondition { - @Override - public boolean isSatisfied() { - return "-Dthin-jar".equals(TestUtil.systemGetEnv("ADDITIONAL_MAVEN_PROFILE")); - } -} diff --git a/src/test/java/net/snowflake/client/TestUtil.java b/src/test/java/net/snowflake/client/TestUtil.java index ba73dbb01..7f4b8d90a 100644 --- a/src/test/java/net/snowflake/client/TestUtil.java +++ b/src/test/java/net/snowflake/client/TestUtil.java @@ -5,10 +5,10 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.hamcrest.Matchers.matchesPattern; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.SQLException; import java.sql.Statement; @@ -19,7 +19,7 @@ import net.snowflake.client.jdbc.SnowflakeUtil; import net.snowflake.client.log.SFLogger; import net.snowflake.client.log.SFLoggerFactory; -import org.junit.Assert; +import org.hamcrest.MatcherAssert; public class TestUtil { private static final SFLogger logger = SFLoggerFactory.getLogger(TestUtil.class); @@ -53,7 +53,7 @@ public static boolean isSchemaGeneratedInTests(String schema) { public static void assertSFException(int errorCode, TestRunInterface testCode) { try { testCode.run(); - Assert.fail(); + fail(); } catch (SFException e) { assertThat(e.getVendorCode(), is(errorCode)); } @@ -91,8 +91,8 @@ public static String systemGetEnv(String env) { public static void assertValidQueryId(String queryId) { assertNotNull(queryId); - assertTrue( - "Expecting " + queryId + " is a valid UUID", QUERY_ID_REGEX.matcher(queryId).matches()); + MatcherAssert.assertThat( + "Expecting " + queryId + " is a valid UUID", queryId, matchesPattern(QUERY_ID_REGEX)); } /** diff --git a/src/test/java/net/snowflake/client/annotations/DontRunOnGithubActions.java b/src/test/java/net/snowflake/client/annotations/DontRunOnGithubActions.java new file mode 100644 index 000000000..993d9d6ad --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/DontRunOnGithubActions.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledIfEnvironmentVariable(named = "GITHUB_ACTIONS", matches = ".*") +public @interface DontRunOnGithubActions {} diff --git a/src/test/java/net/snowflake/client/annotations/DontRunOnJava21.java b/src/test/java/net/snowflake/client/annotations/DontRunOnJava21.java new file mode 100644 index 000000000..29374b837 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/DontRunOnJava21.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledOnJre(JRE.JAVA_21) +public @interface DontRunOnJava21 {} diff --git a/src/test/java/net/snowflake/client/annotations/DontRunOnJava8.java b/src/test/java/net/snowflake/client/annotations/DontRunOnJava8.java new file mode 100644 index 000000000..81a3a0c03 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/DontRunOnJava8.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledOnJre(JRE.JAVA_8) +public @interface DontRunOnJava8 {} diff --git a/src/test/java/net/snowflake/client/annotations/DontRunOnTestaccount.java b/src/test/java/net/snowflake/client/annotations/DontRunOnTestaccount.java new file mode 100644 index 000000000..5c9fff944 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/DontRunOnTestaccount.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledIfEnvironmentVariable(named = "SNOWFLAKE_TEST_ACCOUNT", matches = "testaccount") +public @interface DontRunOnTestaccount {} diff --git a/src/test/java/net/snowflake/client/annotations/DontRunOnThinJar.java b/src/test/java/net/snowflake/client/annotations/DontRunOnThinJar.java new file mode 100644 index 000000000..bb254a2c4 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/DontRunOnThinJar.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledIfEnvironmentVariable(named = "ADDITIONAL_MAVEN_PROFILE", matches = "-Dthin-jar") +public @interface DontRunOnThinJar {} diff --git a/src/test/java/net/snowflake/client/annotations/DontRunOnWindows.java b/src/test/java/net/snowflake/client/annotations/DontRunOnWindows.java new file mode 100644 index 000000000..140f0d752 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/DontRunOnWindows.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@DisabledOnOs(OS.WINDOWS) +public @interface DontRunOnWindows {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnAWS.java b/src/test/java/net/snowflake/client/annotations/RunOnAWS.java new file mode 100644 index 000000000..fd3acc546 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnAWS.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledIfEnvironmentVariable(named = "CLOUD_PROVIDER", matches = "(?i)AWS(?-i)") +public @interface RunOnAWS {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnAzure.java b/src/test/java/net/snowflake/client/annotations/RunOnAzure.java new file mode 100644 index 000000000..13c8379b3 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnAzure.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledIfEnvironmentVariable(named = "CLOUD_PROVIDER", matches = "(?i)Azure(?-i)") +public @interface RunOnAzure {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnGCP.java b/src/test/java/net/snowflake/client/annotations/RunOnGCP.java new file mode 100644 index 000000000..e361aa808 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnGCP.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledIfEnvironmentVariable(named = "CLOUD_PROVIDER", matches = "(?i)GCP(?-i)") +public @interface RunOnGCP {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnGithubActionsNotMac.java b/src/test/java/net/snowflake/client/annotations/RunOnGithubActionsNotMac.java new file mode 100644 index 000000000..f133022e3 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnGithubActionsNotMac.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; +import org.junit.jupiter.api.condition.OS; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledIfEnvironmentVariable(named = "GITHUB_ACTIONS", matches = ".*") +@DisabledOnOs(OS.MAC) +public @interface RunOnGithubActionsNotMac {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnLinux.java b/src/test/java/net/snowflake/client/annotations/RunOnLinux.java new file mode 100644 index 000000000..33231effe --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnLinux.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledOnOs({OS.LINUX, OS.AIX}) +public @interface RunOnLinux {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnLinuxOrMac.java b/src/test/java/net/snowflake/client/annotations/RunOnLinuxOrMac.java new file mode 100644 index 000000000..6c6013154 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnLinuxOrMac.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledOnOs({OS.MAC, OS.LINUX, OS.AIX}) +public @interface RunOnLinuxOrMac {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnMac.java b/src/test/java/net/snowflake/client/annotations/RunOnMac.java new file mode 100644 index 000000000..a5f18a345 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnMac.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledOnOs(OS.MAC) +public @interface RunOnMac {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnTestaccountNotOnGithubActions.java b/src/test/java/net/snowflake/client/annotations/RunOnTestaccountNotOnGithubActions.java new file mode 100644 index 000000000..6dacdb993 --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnTestaccountNotOnGithubActions.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledIfEnvironmentVariable(named = "SNOWFLAKE_TEST_ACCOUNT", matches = "testaccount") +@DisabledIfEnvironmentVariable(named = "GITHUB_ACTIONS", matches = ".*") +public @interface RunOnTestaccountNotOnGithubActions {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnWindows.java b/src/test/java/net/snowflake/client/annotations/RunOnWindows.java new file mode 100644 index 000000000..69a2ee7ff --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnWindows.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledOnOs(OS.WINDOWS) +public @interface RunOnWindows {} diff --git a/src/test/java/net/snowflake/client/annotations/RunOnWindowsOrMac.java b/src/test/java/net/snowflake/client/annotations/RunOnWindowsOrMac.java new file mode 100644 index 000000000..77d50109c --- /dev/null +++ b/src/test/java/net/snowflake/client/annotations/RunOnWindowsOrMac.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@EnabledOnOs({OS.WINDOWS, OS.MAC}) +public @interface RunOnWindowsOrMac {} diff --git a/src/test/java/net/snowflake/client/category/TestCategoryArrow.java b/src/test/java/net/snowflake/client/category/TestCategoryArrow.java deleted file mode 100644 index 59a8396cd..000000000 --- a/src/test/java/net/snowflake/client/category/TestCategoryArrow.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.snowflake.client.category; - -public interface TestCategoryArrow {} diff --git a/src/test/java/net/snowflake/client/category/TestCategoryConnection.java b/src/test/java/net/snowflake/client/category/TestCategoryConnection.java deleted file mode 100644 index cfa5bfd30..000000000 --- a/src/test/java/net/snowflake/client/category/TestCategoryConnection.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.snowflake.client.category; - -public interface TestCategoryConnection {} diff --git a/src/test/java/net/snowflake/client/category/TestCategoryCore.java b/src/test/java/net/snowflake/client/category/TestCategoryCore.java deleted file mode 100644 index 7c97c58ef..000000000 --- a/src/test/java/net/snowflake/client/category/TestCategoryCore.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.snowflake.client.category; - -public interface TestCategoryCore {} diff --git a/src/test/java/net/snowflake/client/category/TestCategoryDiagnostic.java b/src/test/java/net/snowflake/client/category/TestCategoryDiagnostic.java deleted file mode 100644 index ecb5c0509..000000000 --- a/src/test/java/net/snowflake/client/category/TestCategoryDiagnostic.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.snowflake.client.category; - -public interface TestCategoryDiagnostic {} diff --git a/src/test/java/net/snowflake/client/category/TestCategoryLoader.java b/src/test/java/net/snowflake/client/category/TestCategoryLoader.java deleted file mode 100644 index eac9e7bef..000000000 --- a/src/test/java/net/snowflake/client/category/TestCategoryLoader.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.snowflake.client.category; - -/** Test category Loader */ -public interface TestCategoryLoader {} diff --git a/src/test/java/net/snowflake/client/category/TestCategoryOthers.java b/src/test/java/net/snowflake/client/category/TestCategoryOthers.java deleted file mode 100644 index 7f11baaa9..000000000 --- a/src/test/java/net/snowflake/client/category/TestCategoryOthers.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.snowflake.client.category; - -public interface TestCategoryOthers {} diff --git a/src/test/java/net/snowflake/client/category/TestCategoryResultSet.java b/src/test/java/net/snowflake/client/category/TestCategoryResultSet.java deleted file mode 100644 index 7d9824823..000000000 --- a/src/test/java/net/snowflake/client/category/TestCategoryResultSet.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.snowflake.client.category; - -public interface TestCategoryResultSet {} diff --git a/src/test/java/net/snowflake/client/category/TestCategoryStatement.java b/src/test/java/net/snowflake/client/category/TestCategoryStatement.java deleted file mode 100644 index 5381cbb00..000000000 --- a/src/test/java/net/snowflake/client/category/TestCategoryStatement.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.snowflake.client.category; - -public interface TestCategoryStatement {} diff --git a/src/test/java/net/snowflake/client/category/TestTags.java b/src/test/java/net/snowflake/client/category/TestTags.java new file mode 100644 index 000000000..92cd7ce3b --- /dev/null +++ b/src/test/java/net/snowflake/client/category/TestTags.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.category; + +public class TestTags { + private TestTags() {} + + public static final String ARROW = "arrow"; + public static final String CONNECTION = "connection"; + public static final String CORE = "core"; + public static final String DIAGNOSTIC = "diagnostic"; + public static final String LOADER = "loader"; + public static final String OTHERS = "others"; + public static final String RESULT_SET = "resultSet"; + public static final String STATEMENT = "statement"; +} diff --git a/src/test/java/net/snowflake/client/config/SFClientConfigParserTest.java b/src/test/java/net/snowflake/client/config/SFClientConfigParserTest.java index a00784f68..f570cfb7f 100644 --- a/src/test/java/net/snowflake/client/config/SFClientConfigParserTest.java +++ b/src/test/java/net/snowflake/client/config/SFClientConfigParserTest.java @@ -7,10 +7,10 @@ import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; import static net.snowflake.client.jdbc.SnowflakeUtil.systemSetEnv; import static net.snowflake.client.jdbc.SnowflakeUtil.systemUnsetEnv; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mockStatic; import java.io.IOException; @@ -18,8 +18,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import net.snowflake.client.jdbc.SnowflakeUtil; -import org.junit.After; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; public class SFClientConfigParserTest { @@ -30,7 +30,7 @@ public class SFClientConfigParserTest { private Path configFilePath; - @After + @AfterEach public void cleanup() throws IOException { if (configFilePath != null) { Files.deleteIfExists(configFilePath); diff --git a/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java b/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java index bfb30f645..50dd75ff2 100644 --- a/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java +++ b/src/test/java/net/snowflake/client/config/SFConnectionConfigParserTest.java @@ -1,13 +1,13 @@ package net.snowflake.client.config; +import static net.snowflake.client.AssumptionUtils.assumeRunningOnLinuxMac; import static net.snowflake.client.config.SFConnectionConfigParser.SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION; import static net.snowflake.client.config.SFConnectionConfigParser.SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY; import static net.snowflake.client.config.SFConnectionConfigParser.SNOWFLAKE_HOME_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assume.assumeFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import com.fasterxml.jackson.dataformat.toml.TomlMapper; import java.io.File; @@ -25,14 +25,12 @@ import java.util.List; import java.util.Map; import java.util.Set; -import net.snowflake.client.RunningNotOnLinuxMac; import net.snowflake.client.core.Constants; import net.snowflake.client.jdbc.SnowflakeSQLException; import net.snowflake.client.jdbc.SnowflakeUtil; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class SFConnectionConfigParserTest { @@ -46,7 +44,7 @@ public class SFConnectionConfigParserTest { private TomlMapper tomlMapper = new TomlMapper(); private Map envVariables = new HashMap(); - @Before + @BeforeEach public void setUp() throws IOException { tempPath = Files.createTempDirectory(".snowflake"); ENV_VARIABLES_KEYS.stream() @@ -58,7 +56,7 @@ public void setUp() throws IOException { }); } - @After + @AfterEach public void close() throws IOException { SnowflakeUtil.systemUnsetEnv(SNOWFLAKE_HOME_KEY); SnowflakeUtil.systemUnsetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY); @@ -107,7 +105,7 @@ public void testThrowErrorWhenWrongPermissionsForConnectionConfigurationFile() File tokenFile = new File(Paths.get(tempPath.toString(), "token").toUri()); prepareConnectionConfigurationTomlFile( Collections.singletonMap("token_file_path", tokenFile.toString()), false, false); - assumeFalse(RunningNotOnLinuxMac.isNotRunningOnLinuxMac()); + assumeRunningOnLinuxMac(); assertThrows( SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters()); } @@ -118,7 +116,7 @@ public void testThrowErrorWhenWrongPermissionsForTokenFile() throws IOException File tokenFile = new File(Paths.get(tempPath.toString(), "token").toUri()); prepareConnectionConfigurationTomlFile( Collections.singletonMap("token_file_path", tokenFile.toString()), true, false); - assumeFalse(RunningNotOnLinuxMac.isNotRunningOnLinuxMac()); + assumeRunningOnLinuxMac(); assertThrows( SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters()); } @@ -164,7 +162,7 @@ public void shouldThrowExceptionIfNoneOfHostAndAccountIsSet() throws IOException extraparams.put("host", null); extraparams.put("account", null); prepareConnectionConfigurationTomlFile(extraparams); - Assert.assertThrows( + assertThrows( SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters()); } @@ -177,7 +175,7 @@ public void shouldThrowExceptionIfTokenIsNotSetForOauth() throws IOException { prepareConnectionConfigurationTomlFile( Collections.singletonMap("token_file_path", tokenFile.toString()), true, false, ""); - Assert.assertThrows( + assertThrows( SnowflakeSQLException.class, () -> SFConnectionConfigParser.buildConnectionParameters()); } diff --git a/src/test/java/net/snowflake/client/config/SFPermissionsTest.java b/src/test/java/net/snowflake/client/config/SFPermissionsTest.java index 92ec8a624..f5e41e260 100644 --- a/src/test/java/net/snowflake/client/config/SFPermissionsTest.java +++ b/src/test/java/net/snowflake/client/config/SFPermissionsTest.java @@ -1,79 +1,55 @@ package net.snowflake.client.config; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermissions; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnWin; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import net.snowflake.client.annotations.DontRunOnWindows; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; -@RunWith(Parameterized.class) public class SFPermissionsTest { - @Rule public ConditionalIgnoreRule rule = new ConditionalIgnoreRule(); - - @Parameterized.Parameters(name = "permission={0}") - public static Set> data() { - Map testConfigFilePermissions = - new HashMap() { - { - put("rwx------", false); - put("rw-------", false); - put("r-x------", false); - put("r--------", false); - put("rwxrwx---", true); - put("rwxrw----", true); - put("rwxr-x---", false); - put("rwxr-----", false); - put("rwx-wx---", true); - put("rwx-w----", true); - put("rwx--x---", false); - put("rwx---rwx", true); - put("rwx---rw-", true); - put("rwx---r-x", false); - put("rwx---r--", false); - put("rwx----wx", true); - put("rwx----w-", true); - put("rwx-----x", false); - } - }; - return testConfigFilePermissions.entrySet(); - } - Path configFilePath = Paths.get("config.json"); String configJson = "{\"common\":{\"log_level\":\"debug\",\"log_path\":\"logs\"}}"; - String permission; - Boolean isSucceed; - - public SFPermissionsTest(Map.Entry permission) { - this.permission = permission.getKey(); - this.isSucceed = permission.getValue(); - } - @Before + @BeforeEach public void createConfigFile() throws IOException { Files.write(configFilePath, configJson.getBytes()); } - @After + @AfterEach public void cleanupConfigFile() throws IOException { Files.deleteIfExists(configFilePath); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnWin.class) - public void testLogDirectoryPermissions() throws IOException { + @ParameterizedTest + @CsvSource({ + "rwx------,false", + "rw-------,false", + "r-x------,false", + "r--------,false", + "rwxrwx---,true", + "rwxrw----,true", + "rwxr-x---,false", + "rwxr-----,false", + "rwx-wx---,true", + "rwx-w----,true", + "rwx--x---,false", + "rwx---rwx,true", + "rwx---rw-,true", + "rwx---r-x,false", + "rwx---r--,false", + "rwx----wx,true", + "rwx----w-,true", + "rwx-----x,false" + }) + @DontRunOnWindows + public void testLogDirectoryPermissions(String permission, boolean isSucceed) throws IOException { // TODO: SNOW-1503722 Change to check for thrown exceptions // Don't run on Windows Files.setPosixFilePermissions(configFilePath, PosixFilePermissions.fromString(permission)); diff --git a/src/test/java/net/snowflake/client/core/CoreUtilsMiscellaneousTest.java b/src/test/java/net/snowflake/client/core/CoreUtilsMiscellaneousTest.java index f11614c8b..beb0ad292 100644 --- a/src/test/java/net/snowflake/client/core/CoreUtilsMiscellaneousTest.java +++ b/src/test/java/net/snowflake/client/core/CoreUtilsMiscellaneousTest.java @@ -4,10 +4,10 @@ package net.snowflake.client.core; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.amazonaws.ClientConfiguration; import com.amazonaws.Protocol; @@ -16,13 +16,12 @@ import java.net.Proxy; import java.util.HashMap; import java.util.Properties; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.annotations.DontRunOnGithubActions; import net.snowflake.client.jdbc.ErrorCode; import net.snowflake.client.jdbc.SnowflakeSQLException; import net.snowflake.client.jdbc.SnowflakeUtil; import net.snowflake.client.jdbc.cloud.storage.S3HttpUtil; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class CoreUtilsMiscellaneousTest { @@ -41,7 +40,7 @@ public void testSnowflakeAssertTrue() { /** Test that Constants.getOS function is working as expected */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testgetOS() { Constants.clearOSForTesting(); String originalOS = systemGetProperty("os.name"); diff --git a/src/test/java/net/snowflake/client/core/EventHandlerTest.java b/src/test/java/net/snowflake/client/core/EventHandlerTest.java index eb930f7c6..56b48b987 100644 --- a/src/test/java/net/snowflake/client/core/EventHandlerTest.java +++ b/src/test/java/net/snowflake/client/core/EventHandlerTest.java @@ -3,8 +3,8 @@ */ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; @@ -14,18 +14,17 @@ import java.util.logging.LogRecord; import java.util.zip.GZIPInputStream; import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; public class EventHandlerTest { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; - @Before + @BeforeEach public void setUp() throws IOException { - tmpFolder.newFolder("snowflake_dumps"); - System.setProperty("snowflake.dump_path", tmpFolder.getRoot().getCanonicalPath()); + new File(tmpFolder, "snowflake_dumps").mkdirs(); + System.setProperty("snowflake.dump_path", tmpFolder.getCanonicalPath()); } @Test diff --git a/src/test/java/net/snowflake/client/core/EventTest.java b/src/test/java/net/snowflake/client/core/EventTest.java index e9ee978e5..7ca041744 100644 --- a/src/test/java/net/snowflake/client/core/EventTest.java +++ b/src/test/java/net/snowflake/client/core/EventTest.java @@ -5,8 +5,8 @@ package net.snowflake.client.core; import static net.snowflake.client.core.EventUtil.DUMP_PATH_PROP; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; @@ -14,24 +14,25 @@ import java.nio.file.Files; import java.util.zip.GZIPInputStream; import org.apache.commons.io.IOUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; public class EventTest { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; private File homeDirectory; private File dmpDirectory; - @Before + @BeforeEach public void setUp() throws IOException { - homeDirectory = tmpFolder.newFolder("homedir"); - dmpDirectory = tmpFolder.newFolder("homedir", "snowflake_dumps"); + homeDirectory = new File(tmpFolder, "homedir"); + homeDirectory.mkdirs(); + dmpDirectory = new File(homeDirectory, "snowflake_dumps"); + dmpDirectory.mkdirs(); } - @After + @AfterEach public void tearDown() { dmpDirectory.delete(); } @@ -58,7 +59,7 @@ public void testWriteEventDumpLine() throws IOException { // created String dmpPath1 = EventUtil.getDumpPathPrefix(); String dmpPath2 = dmpDirectory.getCanonicalPath(); - assertEquals("dump path is: " + EventUtil.getDumpPathPrefix(), dmpPath2, dmpPath1); + assertEquals(dmpPath2, dmpPath1, "dump path is: " + EventUtil.getDumpPathPrefix()); File dumpFile = new File( EventUtil.getDumpPathPrefix() diff --git a/src/test/java/net/snowflake/client/core/ExecTimeTelemetryDataTest.java b/src/test/java/net/snowflake/client/core/ExecTimeTelemetryDataTest.java index f7ad06b46..04cec29fb 100644 --- a/src/test/java/net/snowflake/client/core/ExecTimeTelemetryDataTest.java +++ b/src/test/java/net/snowflake/client/core/ExecTimeTelemetryDataTest.java @@ -1,14 +1,14 @@ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import net.minidev.json.JSONObject; import net.minidev.json.parser.JSONParser; import net.minidev.json.parser.ParseException; import net.snowflake.client.jdbc.telemetryOOB.TelemetryService; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class ExecTimeTelemetryDataTest { diff --git a/src/test/java/net/snowflake/client/core/HttpUtilLatestIT.java b/src/test/java/net/snowflake/client/core/HttpUtilLatestIT.java index 34892843c..00c318227 100644 --- a/src/test/java/net/snowflake/client/core/HttpUtilLatestIT.java +++ b/src/test/java/net/snowflake/client/core/HttpUtilLatestIT.java @@ -3,21 +3,22 @@ */ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.net.SocketTimeoutException; import java.time.Duration; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.category.TestTags; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class HttpUtilLatestIT { private static final String HANG_WEBSERVER_ADDRESS = "http://localhost:12345/hang"; @@ -30,7 +31,8 @@ public void shouldGetDefaultConnectionAndSocketTimeouts() { } /** Added in > 3.14.5 */ - @Test(timeout = 1000L) + @Test + @Timeout(1) public void shouldOverrideConnectionAndSocketTimeouts() { // it's hard to test connection timeout so there is only a test for socket timeout HttpUtil.setConnectionTimeout(100); diff --git a/src/test/java/net/snowflake/client/core/IncidentUtilLatestIT.java b/src/test/java/net/snowflake/client/core/IncidentUtilLatestIT.java index cd2e89806..5ffe7c5d3 100644 --- a/src/test/java/net/snowflake/client/core/IncidentUtilLatestIT.java +++ b/src/test/java/net/snowflake/client/core/IncidentUtilLatestIT.java @@ -6,23 +6,23 @@ import static net.snowflake.client.core.IncidentUtil.INC_DUMP_FILE_EXT; import static net.snowflake.client.core.IncidentUtil.INC_DUMP_FILE_NAME; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.StringWriter; import java.util.zip.GZIPInputStream; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.BaseJDBCTest; import org.apache.commons.io.IOUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class IncidentUtilLatestIT extends BaseJDBCTest { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; private static final String FILE_NAME = "sf_incident_123456.dmp.gz"; @Test @@ -34,7 +34,9 @@ public void testOneLinerDescription() { /** Tests dumping JVM metrics for the current process */ @Test public void testDumpVmMetrics() throws IOException { - String dumpPath = tmpFolder.newFolder().getCanonicalPath(); + File dumpDir = new File(tmpFolder, "dump"); + dumpDir.mkdirs(); + String dumpPath = dumpDir.getCanonicalPath(); System.setProperty("snowflake.dump_path", dumpPath); String incidentId = "123456"; @@ -47,13 +49,15 @@ public void testDumpVmMetrics() throws IOException { EventUtil.getDumpPathPrefix() + "/" + INC_DUMP_FILE_NAME + incidentId + INC_DUMP_FILE_EXT; // Read back the file contents - GZIPInputStream gzip = new GZIPInputStream(new FileInputStream(targetVMFileLocation)); - StringWriter sWriter = new StringWriter(); - IOUtils.copy(gzip, sWriter, "UTF-8"); - String output = sWriter.toString(); - assertEquals( - "\n\n\n--------------------------- METRICS " + "---------------------------\n\n", - output.substring(0, 69)); - sWriter.close(); + try (FileInputStream fis = new FileInputStream(targetVMFileLocation); + GZIPInputStream gzip = new GZIPInputStream(fis)) { + StringWriter sWriter = new StringWriter(); + IOUtils.copy(gzip, sWriter, "UTF-8"); + String output = sWriter.toString(); + assertEquals( + "\n\n\n--------------------------- METRICS " + "---------------------------\n\n", + output.substring(0, 69)); + sWriter.close(); + } } } diff --git a/src/test/java/net/snowflake/client/core/OCSPCacheServerTest.java b/src/test/java/net/snowflake/client/core/OCSPCacheServerTest.java index 9a5af03b2..37bfea5c6 100644 --- a/src/test/java/net/snowflake/client/core/OCSPCacheServerTest.java +++ b/src/test/java/net/snowflake/client/core/OCSPCacheServerTest.java @@ -1,93 +1,76 @@ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import java.util.stream.Stream; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class OCSPCacheServerTest { - @Parameterized.Parameters( - name = "For host {0} cache server fetch url should be {1} and retry url {2}") - public static Object[][] data() { - return new Object[][] { - { - "bla-12345.global.snowflakecomputing.com", - "https://ocspssd-12345.global.snowflakecomputing.com/ocsp/fetch", - "https://ocspssd-12345.global.snowflakecomputing.com/ocsp/retry" - }, - { - "bla-12345.global.snowflakecomputing.cn", - "https://ocspssd-12345.global.snowflakecomputing.cn/ocsp/fetch", - "https://ocspssd-12345.global.snowflakecomputing.cn/ocsp/retry" - }, - { - "bla-12345.global.snowflakecomputing.xyz", - "https://ocspssd-12345.global.snowflakecomputing.xyz/ocsp/fetch", - "https://ocspssd-12345.global.snowflakecomputing.xyz/ocsp/retry" - }, - { - "bla-12345.GLOBAL.snowflakecomputing.xyz", - "https://ocspssd-12345.GLOBAL.snowflakecomputing.xyz/ocsp/fetch", - "https://ocspssd-12345.GLOBAL.snowflakecomputing.xyz/ocsp/retry" - }, - { - "bla-12345.snowflakecomputing.com", - "https://ocspssd.snowflakecomputing.com/ocsp/fetch", - "https://ocspssd.snowflakecomputing.com/ocsp/retry" - }, - { - "bla-12345.snowflakecomputing.cn", - "https://ocspssd.snowflakecomputing.cn/ocsp/fetch", - "https://ocspssd.snowflakecomputing.cn/ocsp/retry" - }, - { - "bla-12345.snowflakecomputing.xyz", - "https://ocspssd.snowflakecomputing.xyz/ocsp/fetch", - "https://ocspssd.snowflakecomputing.xyz/ocsp/retry" - }, - { - "bla-12345.SNOWFLAKEcomputing.xyz", - "https://ocspssd.SNOWFLAKEcomputing.xyz/ocsp/fetch", - "https://ocspssd.SNOWFLAKEcomputing.xyz/ocsp/retry" - }, - { - "s3.amazoncomaws.com", - "https://ocspssd.snowflakecomputing.com/ocsp/fetch", - "https://ocspssd.snowflakecomputing.com/ocsp/retry" - }, - { - "s3.amazoncomaws.COM", - "https://ocspssd.snowflakecomputing.COM/ocsp/fetch", - "https://ocspssd.snowflakecomputing.COM/ocsp/retry" - }, - { - "s3.amazoncomaws.com.cn", - "https://ocspssd.snowflakecomputing.cn/ocsp/fetch", - "https://ocspssd.snowflakecomputing.cn/ocsp/retry" - }, - { - "S3.AMAZONCOMAWS.COM.CN", - "https://ocspssd.snowflakecomputing.CN/ocsp/fetch", - "https://ocspssd.snowflakecomputing.CN/ocsp/retry" - }, - }; - } - - private final String host; - private final String expectedFetchUrl; - private final String expectedRetryUrl; + static class URLProvider implements ArgumentsProvider { - public OCSPCacheServerTest(String host, String expectedFetchUrl, String expectedRetryUrl) { - this.host = host; - this.expectedFetchUrl = expectedFetchUrl; - this.expectedRetryUrl = expectedRetryUrl; + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of( + "bla-12345.global.snowflakecomputing.com", + "https://ocspssd-12345.global.snowflakecomputing.com/ocsp/fetch", + "https://ocspssd-12345.global.snowflakecomputing.com/ocsp/retry"), + Arguments.of( + "bla-12345.global.snowflakecomputing.cn", + "https://ocspssd-12345.global.snowflakecomputing.cn/ocsp/fetch", + "https://ocspssd-12345.global.snowflakecomputing.cn/ocsp/retry"), + Arguments.of( + "bla-12345.global.snowflakecomputing.xyz", + "https://ocspssd-12345.global.snowflakecomputing.xyz/ocsp/fetch", + "https://ocspssd-12345.global.snowflakecomputing.xyz/ocsp/retry"), + Arguments.of( + "bla-12345.GLOBAL.snowflakecomputing.xyz", + "https://ocspssd-12345.GLOBAL.snowflakecomputing.xyz/ocsp/fetch", + "https://ocspssd-12345.GLOBAL.snowflakecomputing.xyz/ocsp/retry"), + Arguments.of( + "bla-12345.snowflakecomputing.com", + "https://ocspssd.snowflakecomputing.com/ocsp/fetch", + "https://ocspssd.snowflakecomputing.com/ocsp/retry"), + Arguments.of( + "bla-12345.snowflakecomputing.cn", + "https://ocspssd.snowflakecomputing.cn/ocsp/fetch", + "https://ocspssd.snowflakecomputing.cn/ocsp/retry"), + Arguments.of( + "bla-12345.snowflakecomputing.xyz", + "https://ocspssd.snowflakecomputing.xyz/ocsp/fetch", + "https://ocspssd.snowflakecomputing.xyz/ocsp/retry"), + Arguments.of( + "bla-12345.SNOWFLAKEcomputing.xyz", + "https://ocspssd.SNOWFLAKEcomputing.xyz/ocsp/fetch", + "https://ocspssd.SNOWFLAKEcomputing.xyz/ocsp/retry"), + Arguments.of( + "s3.amazoncomaws.com", + "https://ocspssd.snowflakecomputing.com/ocsp/fetch", + "https://ocspssd.snowflakecomputing.com/ocsp/retry"), + Arguments.of( + "s3.amazoncomaws.COM", + "https://ocspssd.snowflakecomputing.COM/ocsp/fetch", + "https://ocspssd.snowflakecomputing.COM/ocsp/retry"), + Arguments.of( + "s3.amazoncomaws.com.cn", + "https://ocspssd.snowflakecomputing.cn/ocsp/fetch", + "https://ocspssd.snowflakecomputing.cn/ocsp/retry"), + Arguments.of( + "S3.AMAZONCOMAWS.COM.CN", + "https://ocspssd.snowflakecomputing.CN/ocsp/fetch", + "https://ocspssd.snowflakecomputing.CN/ocsp/retry")); + } } - @Test - public void shouldChooseOcspCacheServerUrls() { + @ParameterizedTest(name = "For host {0} cache server fetch url should be {1} and retry url {2}") + @ArgumentsSource(URLProvider.class) + public void shouldChooseOcspCacheServerUrls( + String host, String expectedFetchUrl, String expectedRetryUrl) { SFTrustManager.OCSPCacheServer ocspCacheServer = new SFTrustManager.OCSPCacheServer(); ocspCacheServer.resetOCSPResponseCacheServer(host); diff --git a/src/test/java/net/snowflake/client/core/ObjectMapperTest.java b/src/test/java/net/snowflake/client/core/ObjectMapperTest.java index 6868d186e..e0a9e11ab 100644 --- a/src/test/java/net/snowflake/client/core/ObjectMapperTest.java +++ b/src/test/java/net/snowflake/client/core/ObjectMapperTest.java @@ -4,52 +4,58 @@ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.charset.StandardCharsets; import java.sql.SQLException; -import java.util.ArrayList; import java.util.Base64; -import java.util.Collection; -import java.util.List; +import java.util.stream.Stream; import net.snowflake.client.jdbc.SnowflakeUtil; -import org.junit.After; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class ObjectMapperTest { private static final int jacksonDefaultMaxStringLength = 20_000_000; + static String originalLogger; - @Parameterized.Parameters(name = "lobSizeInMB={0}, maxJsonStringLength={1}") - public static Collection data() { - int[] lobSizeInMB = new int[] {16, 16, 32, 64, 128}; - // maxJsonStringLength to be set for the corresponding LOB size - int[] maxJsonStringLengths = - new int[] {jacksonDefaultMaxStringLength, 23_000_000, 45_000_000, 90_000_000, 180_000_000}; - List ret = new ArrayList<>(); - for (int i = 0; i < lobSizeInMB.length; i++) { - ret.add(new Object[] {lobSizeInMB[i], maxJsonStringLengths[i]}); + static class DataProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of(16 * 1024 * 1024, jacksonDefaultMaxStringLength), + Arguments.of(16 * 1024 * 1024, 23_000_000), + Arguments.of(32 * 1024 * 1024, 45_000_000), + Arguments.of(64 * 1024 * 1024, 90_000_000), + Arguments.of(128 * 1024 * 1024, 180_000_000)); } - return ret; } - private final int lobSizeInBytes; - private final int maxJsonStringLength; + @BeforeAll + public static void setProperty() { + originalLogger = System.getProperty("net.snowflake.jdbc.loggerImpl"); + System.setProperty("net.snowflake.jdbc.loggerImpl", "net.snowflake.client.log.JDK14Logger"); + } - @After - public void clearProperty() { + @AfterAll + public static void clearProperty() { + if (originalLogger != null) { + System.setProperty("net.snowflake.jdbc.loggerImpl", originalLogger); + } else { + System.clearProperty("net.snowflake.jdbc.loggerImpl"); + } System.clearProperty(ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM); } - public ObjectMapperTest(int lobSizeInMB, int maxJsonStringLength) { - // convert LOB size from MB to bytes - this.lobSizeInBytes = lobSizeInMB * 1024 * 1024; - this.maxJsonStringLength = maxJsonStringLength; + private static void setJacksonDefaultMaxStringLength(int maxJsonStringLength) { System.setProperty( ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM, Integer.toString(maxJsonStringLength)); } @@ -61,15 +67,17 @@ public void testInvalidMaxJsonStringLength() throws SQLException { // default maxJsonStringLength value will be used ObjectMapper mapper = ObjectMapperFactory.getObjectMapper(); int stringLengthInMapper = mapper.getFactory().streamReadConstraints().getMaxStringLength(); - Assert.assertEquals(ObjectMapperFactory.DEFAULT_MAX_JSON_STRING_LEN, stringLengthInMapper); + assertEquals(ObjectMapperFactory.DEFAULT_MAX_JSON_STRING_LEN, stringLengthInMapper); } - @Test - public void testObjectMapperWithLargeJsonString() { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testObjectMapperWithLargeJsonString(int lobSizeInBytes, int maxJsonStringLength) { + setJacksonDefaultMaxStringLength(maxJsonStringLength); ObjectMapper mapper = ObjectMapperFactory.getObjectMapper(); try { JsonNode jsonNode = mapper.readTree(generateBase64EncodedJsonString(lobSizeInBytes)); - Assert.assertNotNull(jsonNode); + assertNotNull(jsonNode); } catch (Exception e) { // exception is expected when jackson's default maxStringLength value is used while retrieving // 16M string data diff --git a/src/test/java/net/snowflake/client/core/PrivateLinkDetectorTest.java b/src/test/java/net/snowflake/client/core/PrivateLinkDetectorTest.java index b3af68011..d5afb1af5 100644 --- a/src/test/java/net/snowflake/client/core/PrivateLinkDetectorTest.java +++ b/src/test/java/net/snowflake/client/core/PrivateLinkDetectorTest.java @@ -1,42 +1,38 @@ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import java.util.stream.Stream; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class PrivateLinkDetectorTest { + static class DataProvider implements ArgumentsProvider { - @Parameterized.Parameters(name = "Host {0} is private link: {1}") - public static Object[][] data() { - return new Object[][] { - {"snowhouse.snowflakecomputing.com", false}, - {"snowhouse.privatelink.snowflakecomputing.com", true}, - {"snowhouse.PRIVATELINK.snowflakecomputing.com", true}, - {"snowhouse.snowflakecomputing.cn", false}, - {"snowhouse.privatelink.snowflakecomputing.cn", true}, - {"snowhouse.PRIVATELINK.snowflakecomputing.cn", true}, - {"snowhouse.snowflakecomputing.xyz", false}, - {"snowhouse.privatelink.snowflakecomputing.xyz", true}, - {"snowhouse.PRIVATELINK.snowflakecomputing.xyz", true}, - }; + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of("snowhouse.snowflakecomputing.com", false), + Arguments.of("snowhouse.privatelink.snowflakecomputing.com", true), + Arguments.of("snowhouse.PRIVATELINK.snowflakecomputing.com", true), + Arguments.of("snowhouse.snowflakecomputing.cn", false), + Arguments.of("snowhouse.privatelink.snowflakecomputing.cn", true), + Arguments.of("snowhouse.PRIVATELINK.snowflakecomputing.cn", true), + Arguments.of("snowhouse.snowflakecomputing.xyz", false), + Arguments.of("snowhouse.privatelink.snowflakecomputing.xyz", true), + Arguments.of("snowhouse.PRIVATELINK.snowflakecomputing.xyz", true)); + } } - private final String host; - private final boolean expectedToBePrivateLink; - - public PrivateLinkDetectorTest(String host, boolean expectedToBePrivateLink) { - this.host = host; - this.expectedToBePrivateLink = expectedToBePrivateLink; - } - - @Test - public void shouldDetectPrivateLinkHost() { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void shouldDetectPrivateLinkHost(String host, boolean expectedToBePrivateLink) { assertEquals( - String.format("Expecting %s to be private link: %s", host, expectedToBePrivateLink), expectedToBePrivateLink, - PrivateLinkDetector.isPrivateLink(host)); + PrivateLinkDetector.isPrivateLink(host), + String.format("Expecting %s to be private link: %s", host, expectedToBePrivateLink)); } } diff --git a/src/test/java/net/snowflake/client/core/QueryContextCacheTest.java b/src/test/java/net/snowflake/client/core/QueryContextCacheTest.java index 862dd1c40..e13ecd673 100644 --- a/src/test/java/net/snowflake/client/core/QueryContextCacheTest.java +++ b/src/test/java/net/snowflake/client/core/QueryContextCacheTest.java @@ -5,12 +5,12 @@ package net.snowflake.client.core; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class QueryContextCacheTest { private QueryContextCache qcc = null; diff --git a/src/test/java/net/snowflake/client/core/SFArrowResultSetIT.java b/src/test/java/net/snowflake/client/core/SFArrowResultSetIT.java index af6ac5219..8be8fd471 100644 --- a/src/test/java/net/snowflake/client/core/SFArrowResultSetIT.java +++ b/src/test/java/net/snowflake/client/core/SFArrowResultSetIT.java @@ -6,8 +6,8 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import java.io.File; import java.io.FileInputStream; @@ -26,9 +26,8 @@ import java.util.List; import java.util.Map; import java.util.Random; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.SkipOnThinJar; -import net.snowflake.client.category.TestCategoryArrow; +import net.snowflake.client.annotations.DontRunOnThinJar; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.ArrowResultChunk; import net.snowflake.client.jdbc.BaseJDBCWithSharedConnectionIT; import net.snowflake.client.jdbc.ErrorCode; @@ -63,17 +62,12 @@ import org.apache.arrow.vector.types.pojo.Schema; import org.apache.arrow.vector.util.Text; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; -@Category(TestCategoryArrow.class) +@Tag(TestTags.ARROW) public class SFArrowResultSetIT extends BaseJDBCWithSharedConnectionIT { - - /** Necessary to conditional ignore tests */ - @Rule public ConditionalIgnoreRule rule = new ConditionalIgnoreRule(); - private Random random = new Random(); /** @@ -83,11 +77,11 @@ public class SFArrowResultSetIT extends BaseJDBCWithSharedConnectionIT { protected BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); /** temporary folder to store result files */ - @Rule public TemporaryFolder resultFolder = new TemporaryFolder(); + @TempDir private File tempDir; /** Test the case that all results are returned in first chunk */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = SkipOnThinJar.class) + @DontRunOnThinJar public void testNoOfflineData() throws Throwable { List fieldList = new ArrayList<>(); Map customFieldMeta = new HashMap<>(); @@ -103,8 +97,9 @@ public void testNoOfflineData() throws Throwable { int dataSize = (int) file.length(); byte[] dataBytes = new byte[dataSize]; - InputStream is = new FileInputStream(file); - is.read(dataBytes, 0, dataSize); + try (InputStream is = new FileInputStream(file)) { + is.read(dataBytes, 0, dataSize); + } SnowflakeResultSetSerializableV1 resultSetSerializable = new SnowflakeResultSetSerializableV1(); resultSetSerializable.setRootAllocator(new RootAllocator(Long.MAX_VALUE)); @@ -149,7 +144,7 @@ public void testEmptyResultSet() throws Throwable { /** Testing the case that all data comes from chunk downloader */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = SkipOnThinJar.class) + @DontRunOnThinJar public void testOnlyOfflineData() throws Throwable { final int colCount = 2; final int chunkCount = 10; @@ -199,7 +194,7 @@ public void testOnlyOfflineData() throws Throwable { /** Testing the case that all data comes from chunk downloader */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = SkipOnThinJar.class) + @DontRunOnThinJar public void testFirstResponseAndOfflineData() throws Throwable { final int colCount = 2; final int chunkCount = 10; @@ -229,8 +224,9 @@ public void testFirstResponseAndOfflineData() throws Throwable { int dataSize = (int) arrowFile.length(); byte[] dataBytes = new byte[dataSize]; - InputStream is = new FileInputStream(arrowFile); - is.read(dataBytes, 0, dataSize); + try (InputStream is = new FileInputStream(arrowFile)) { + is.read(dataBytes, 0, dataSize); + } SnowflakeResultSetSerializableV1 resultSetSerializable = new SnowflakeResultSetSerializableV1(); resultSetSerializable.setFirstChunkStringData(Base64.getEncoder().encodeToString(dataBytes)); @@ -280,8 +276,7 @@ private class MockChunkDownloader implements ChunkDownloader { public SnowflakeResultChunk getNextChunkToConsume() throws SnowflakeSQLException { if (currentFileIndex < resultFileNames.size()) { ArrowResultChunk resultChunk = new ArrowResultChunk("", 0, 0, 0, rootAllocator, null); - try { - InputStream is = new FileInputStream(resultFileNames.get(currentFileIndex)); + try (InputStream is = new FileInputStream(resultFileNames.get(currentFileIndex))) { resultChunk.readArrowStream(is); currentFileIndex++; @@ -380,12 +375,13 @@ Object[][] generateData(Schema schema, int rowCount) { File createArrowFile(String fileName, Schema schema, Object[][] data, int rowsPerRecordBatch) throws IOException { - File file = resultFolder.newFile(fileName); + File file = new File(tempDir, fileName); + file.createNewFile(); VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator); - try (ArrowWriter writer = - new ArrowStreamWriter( - root, new DictionaryProvider.MapDictionaryProvider(), new FileOutputStream(file))) { + try (FileOutputStream fos = new FileOutputStream(file); + ArrowWriter writer = + new ArrowStreamWriter(root, new DictionaryProvider.MapDictionaryProvider(), fos)) { writer.start(); for (int i = 0; i < data[0].length; ) { @@ -592,7 +588,7 @@ private void writeTimestampStructToField( /** Test that first chunk containing struct vectors (used for timestamps) can be sorted */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = SkipOnThinJar.class) + @DontRunOnThinJar public void testSortedResultChunkWithStructVectors() throws Throwable { try (Statement statement = connection.createStatement()) { statement.execute("create or replace table teststructtimestamp (t1 timestamp_ltz)"); @@ -638,8 +634,9 @@ public void testSortedResultChunkWithStructVectors() throws Throwable { int dataSize = (int) file.length(); byte[] dataBytes = new byte[dataSize]; - InputStream is = new FileInputStream(file); - is.read(dataBytes, 0, dataSize); + try (InputStream is = new FileInputStream(file)) { + is.read(dataBytes, 0, dataSize); + } resultSetSerializable.setRootAllocator(new RootAllocator(Long.MAX_VALUE)); resultSetSerializable.setFirstChunkStringData( @@ -663,7 +660,7 @@ public void testSortedResultChunkWithStructVectors() throws Throwable { /** Test that the first chunk can be sorted */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = SkipOnThinJar.class) + @DontRunOnThinJar public void testSortedResultChunk() throws Throwable { try (Statement statement = connection.createStatement()) { statement.execute( @@ -725,8 +722,9 @@ public void testSortedResultChunk() throws Throwable { int dataSize = (int) file.length(); byte[] dataBytes = new byte[dataSize]; - InputStream is = new FileInputStream(file); - is.read(dataBytes, 0, dataSize); + try (InputStream is = new FileInputStream(file)) { + is.read(dataBytes, 0, dataSize); + } resultSetSerializable.setRootAllocator(new RootAllocator(Long.MAX_VALUE)); resultSetSerializable.setFirstChunkStringData( diff --git a/src/test/java/net/snowflake/client/core/SFLoginInputTest.java b/src/test/java/net/snowflake/client/core/SFLoginInputTest.java index 7d8a5b67b..b34eebc02 100644 --- a/src/test/java/net/snowflake/client/core/SFLoginInputTest.java +++ b/src/test/java/net/snowflake/client/core/SFLoginInputTest.java @@ -1,8 +1,8 @@ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SFLoginInputTest { diff --git a/src/test/java/net/snowflake/client/core/SFSessionPropertyTest.java b/src/test/java/net/snowflake/client/core/SFSessionPropertyTest.java index 8c7a6fb1f..142f92217 100644 --- a/src/test/java/net/snowflake/client/core/SFSessionPropertyTest.java +++ b/src/test/java/net/snowflake/client/core/SFSessionPropertyTest.java @@ -7,10 +7,10 @@ import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.fail; import net.snowflake.client.jdbc.ErrorCode; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SFSessionPropertyTest { @Test @@ -28,7 +28,7 @@ public void testCheckApplicationName() throws SFException { for (String invalid : invalidApplicationName) { try { SFSessionProperty.checkPropertyValue(SFSessionProperty.APPLICATION, invalid); - Assert.fail(); + fail(); } catch (SFException e) { assertThat(e.getVendorCode(), is(ErrorCode.INVALID_PARAMETER_VALUE.getMessageCode())); } @@ -48,7 +48,7 @@ public void testCustomSuffixForUserAgentHeaders() { public void testInvalidMaxRetries() { try { SFSessionProperty.checkPropertyValue(SFSessionProperty.MAX_HTTP_RETRIES, "invalidValue"); - Assert.fail("testInvalidMaxRetries"); + fail("testInvalidMaxRetries"); } catch (SFException e) { assertThat(e.getVendorCode(), is(ErrorCode.INVALID_PARAMETER_VALUE.getMessageCode())); } @@ -67,7 +67,7 @@ public void testvalidMaxRetries() throws SFException { public void testInvalidPutGetMaxRetries() { try { SFSessionProperty.checkPropertyValue(SFSessionProperty.PUT_GET_MAX_RETRIES, "invalidValue"); - Assert.fail("testInvalidMaxRetries"); + fail("testInvalidMaxRetries"); } catch (SFException e) { assertThat(e.getVendorCode(), is(ErrorCode.INVALID_PARAMETER_VALUE.getMessageCode())); } diff --git a/src/test/java/net/snowflake/client/core/SFTrustManagerIT.java b/src/test/java/net/snowflake/client/core/SFTrustManagerIT.java index f30cd88e1..2645277b1 100644 --- a/src/test/java/net/snowflake/client/core/SFTrustManagerIT.java +++ b/src/test/java/net/snowflake/client/core/SFTrustManagerIT.java @@ -20,8 +20,9 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import javax.net.ssl.SSLHandshakeException; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.BaseJDBCTest; import net.snowflake.client.jdbc.telemetryOOB.TelemetryService; import net.snowflake.client.log.SFLogger; @@ -29,45 +30,41 @@ import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class SFTrustManagerIT extends BaseJDBCTest { private static final SFLogger logger = SFLoggerFactory.getLogger(SFTrustManagerIT.class); - public SFTrustManagerIT(String host) { - this.host = host; - } - - @Parameterized.Parameters(name = "host={0}") - public static Object[][] data() { - return new Object[][] { - // this host generates many "SSLHandshake Certificate Revocation - // check failed. Could not retrieve OCSP Response." when running in parallel CI builds - // {"storage.googleapis.com"}, - {"ocspssd.us-east-1.snowflakecomputing.com/ocsp/fetch"}, - {"sfcsupport.snowflakecomputing.com"}, - {"sfcsupport.us-east-1.snowflakecomputing.com"}, - {"sfcsupport.eu-central-1.snowflakecomputing.com"}, - {"sfc-dev1-regression.s3.amazonaws.com"}, - {"sfc-ds2-customer-stage.s3.amazonaws.com"}, - {"snowflake.okta.com"}, - {"sfcdev2.blob.core.windows.net"} - }; + private static class HostProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + // this host generates many "SSLHandshake Certificate Revocation + // check failed. Could not retrieve OCSP Response." when running in parallel CI builds + // Arguments.of("storage.googleapis.com"), + Arguments.of("ocspssd.us-east-1.snowflakecomputing.com/ocsp/fetch"), + Arguments.of("sfcsupport.snowflakecomputing.com"), + Arguments.of("sfcsupport.us-east-1.snowflakecomputing.com"), + Arguments.of("sfcsupport.eu-central-1.snowflakecomputing.com"), + Arguments.of("sfc-dev1-regression.s3.amazonaws.com"), + Arguments.of("sfc-ds2-customer-stage.s3.amazonaws.com"), + Arguments.of("snowflake.okta.com"), + Arguments.of("sfcdev2.blob.core.windows.net")); + } } private boolean defaultState; - private final String host; - @Before + @BeforeEach public void setUp() { TelemetryService service = TelemetryService.getInstance(); service.updateContextForIT(getConnectionParameters()); @@ -76,7 +73,7 @@ public void setUp() { service.enable(); } - @After + @AfterEach public void tearDown() throws InterruptedException { TelemetryService service = TelemetryService.getInstance(); // wait 5 seconds while the service is flushing @@ -90,15 +87,16 @@ public void tearDown() throws InterruptedException { System.clearProperty(SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_URL); } - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir File tmpFolder; /** * OCSP tests for the Snowflake and AWS S3 HTTPS connections. * *

Whatever the default method is used. */ - @Test - public void testOcsp() throws Throwable { + @ParameterizedTest + @ArgumentsSource(HostProvider.class) + public void testOcsp(String host) throws Throwable { System.setProperty( SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED, Boolean.TRUE.toString()); HttpClient client = @@ -115,11 +113,13 @@ public void testOcsp() throws Throwable { * *

Specifying an non-existing file will force to fetch OCSP response. */ - @Test - public void testOcspWithFileCache() throws Throwable { + @ParameterizedTest + @ArgumentsSource(HostProvider.class) + public void testOcspWithFileCache(String host) throws Throwable { System.setProperty( SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED, Boolean.FALSE.toString()); - File ocspCacheFile = tmpFolder.newFile(); + File ocspCacheFile = new File(tmpFolder, "ocsp-cache"); + ocspCacheFile.createNewFile(); HttpClient client = HttpUtil.buildHttpClient( new HttpClientSettingsKey(OCSPMode.FAIL_CLOSED), @@ -130,11 +130,13 @@ public void testOcspWithFileCache() throws Throwable { } /** OCSP tests for the Snowflake and AWS S3 HTTPS connections using the server cache. */ - @Test - public void testOcspWithServerCache() throws Throwable { + @ParameterizedTest + @ArgumentsSource(HostProvider.class) + public void testOcspWithServerCache(String host) throws Throwable { System.setProperty( SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED, Boolean.TRUE.toString()); - File ocspCacheFile = tmpFolder.newFile(); + File ocspCacheFile = new File(tmpFolder, "ocsp-cache"); + ocspCacheFile.createNewFile(); HttpClient client = HttpUtil.buildHttpClient( new HttpClientSettingsKey(OCSPMode.FAIL_CLOSED), @@ -148,11 +150,13 @@ public void testOcspWithServerCache() throws Throwable { * OCSP tests for the Snowflake and AWS S3 HTTPS connections without using the server cache. This * test should always pass - even with OCSP Outage. */ - @Test - public void testOcspWithoutServerCache() throws Throwable { + @ParameterizedTest + @ArgumentsSource(HostProvider.class) + public void testOcspWithoutServerCache(String host) throws Throwable { System.setProperty( SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED, Boolean.FALSE.toString()); - File ocspCacheFile = tmpFolder.newFile(); + File ocspCacheFile = new File(tmpFolder, "ocsp-cache"); + ocspCacheFile.createNewFile(); HttpClient client = HttpUtil.buildHttpClient( new HttpClientSettingsKey(OCSPMode.FAIL_OPEN), @@ -163,8 +167,9 @@ public void testOcspWithoutServerCache() throws Throwable { } /** OCSP tests for the Snowflake and AWS S3 HTTPS connections using the server cache. */ - @Test - public void testInvalidCacheFile() throws Throwable { + @ParameterizedTest + @ArgumentsSource(HostProvider.class) + public void testInvalidCacheFile(String host) throws Throwable { System.setProperty( SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_ENABLED, Boolean.TRUE.toString()); // a file under never exists. diff --git a/src/test/java/net/snowflake/client/core/SFTrustManagerMockitoMockLatestIT.java b/src/test/java/net/snowflake/client/core/SFTrustManagerMockitoMockLatestIT.java index 862f4867e..077ec6829 100644 --- a/src/test/java/net/snowflake/client/core/SFTrustManagerMockitoMockLatestIT.java +++ b/src/test/java/net/snowflake/client/core/SFTrustManagerMockitoMockLatestIT.java @@ -14,32 +14,32 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.SnowflakeUtil; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.mockito.MockedStatic; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class SFTrustManagerMockitoMockLatestIT { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; /* * Test SF_OCSP_RESPONSE_CACHE_DIR environment variable changes the * location of the OCSP cache directory. */ @Test - @Ignore("static initialization block of SFTrustManager class doesn't run sometimes") + @Disabled("static initialization block of SFTrustManager class doesn't run sometimes") public void testUnitOCSPWithCustomCacheDirectory() throws IOException { try (MockedStatic mockedTrustManagerFactory = mockStatic(TrustManagerFactory.class); MockedStatic mockedSnowflakeUtil = mockStatic(SnowflakeUtil.class)) { - File cacheFolder = tmpFolder.newFolder(); + File cacheFolder = new File(tmpFolder, "cache"); + cacheFolder.mkdirs(); mockedSnowflakeUtil .when(() -> TestUtil.systemGetEnv("SF_OCSP_RESPONSE_CACHE_DIR")) .thenReturn(cacheFolder.getCanonicalPath()); diff --git a/src/test/java/net/snowflake/client/core/SFTrustManagerTest.java b/src/test/java/net/snowflake/client/core/SFTrustManagerTest.java index 6a55b2cd4..77a06cb2a 100644 --- a/src/test/java/net/snowflake/client/core/SFTrustManagerTest.java +++ b/src/test/java/net/snowflake/client/core/SFTrustManagerTest.java @@ -11,10 +11,24 @@ import java.util.Properties; import net.snowflake.client.jdbc.SnowflakeResultSetSerializable; import net.snowflake.client.jdbc.SnowflakeResultSetSerializableV1; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class SFTrustManagerTest { /** Test building OCSP retry URL */ + static String originalRetryUrlPattern; + + @BeforeAll + public static void saveStaticValues() { + originalRetryUrlPattern = SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN; + } + + @AfterAll + public static void restoreStaticValues() { + SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN = originalRetryUrlPattern; + } + @Test public void testBuildRetryURL() throws Exception { // private link diff --git a/src/test/java/net/snowflake/client/core/SQLInputOutputTest.java b/src/test/java/net/snowflake/client/core/SQLInputOutputTest.java index 346d43c34..f8224a8eb 100644 --- a/src/test/java/net/snowflake/client/core/SQLInputOutputTest.java +++ b/src/test/java/net/snowflake/client/core/SQLInputOutputTest.java @@ -4,7 +4,7 @@ import static org.mockito.Mockito.mock; import java.sql.SQLData; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SQLInputOutputTest { diff --git a/src/test/java/net/snowflake/client/core/SecureStorageManagerTest.java b/src/test/java/net/snowflake/client/core/SecureStorageManagerTest.java index b6f8a16ac..b79875038 100644 --- a/src/test/java/net/snowflake/client/core/SecureStorageManagerTest.java +++ b/src/test/java/net/snowflake/client/core/SecureStorageManagerTest.java @@ -16,11 +16,11 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningNotOnLinux; -import net.snowflake.client.RunningNotOnWinMac; -import org.junit.Rule; -import org.junit.Test; +import net.snowflake.client.annotations.RunOnLinux; +import net.snowflake.client.annotations.RunOnMac; +import net.snowflake.client.annotations.RunOnWindows; +import net.snowflake.client.annotations.RunOnWindowsOrMac; +import org.junit.jupiter.api.Test; class MockAdvapi32Lib implements SecureStorageWindowsManager.Advapi32Lib { @Override @@ -213,8 +213,6 @@ Pointer getPointer() { } public class SecureStorageManagerTest { - // This is required to use ConditionalIgnore annotation - @Rule public ConditionalIgnoreRule rule = new ConditionalIgnoreRule(); private static final String host = "fakeHost"; private static final String user = "fakeUser"; @@ -227,7 +225,7 @@ public class SecureStorageManagerTest { private static final String MFA_TOKEN = "MFATOKEN"; @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningNotOnWinMac.class) + @RunOnWindowsOrMac public void testLoadNativeLibrary() { // Only run on Mac or Windows. Make sure the loading of native platform library won't break. if (Constants.getOS() == Constants.OS.MAC) { @@ -240,6 +238,7 @@ public void testLoadNativeLibrary() { } @Test + @RunOnWindows public void testWindowsManager() { SecureStorageWindowsManager.Advapi32LibManager.setInstance(new MockAdvapi32Lib()); SecureStorageManager manager = SecureStorageWindowsManager.builder(); @@ -249,6 +248,7 @@ public void testWindowsManager() { } @Test + @RunOnMac public void testMacManager() { SecureStorageAppleManager.SecurityLibManager.setInstance(new MockSecurityLib()); SecureStorageManager manager = SecureStorageAppleManager.builder(); @@ -258,7 +258,7 @@ public void testMacManager() { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningNotOnLinux.class) + @RunOnLinux public void testLinuxManager() { SecureStorageManager manager = SecureStorageLinuxManager.getInstance(); diff --git a/src/test/java/net/snowflake/client/core/SessionUtilExternalBrowserTest.java b/src/test/java/net/snowflake/client/core/SessionUtilExternalBrowserTest.java index 2ba00f378..02f6193d6 100644 --- a/src/test/java/net/snowflake/client/core/SessionUtilExternalBrowserTest.java +++ b/src/test/java/net/snowflake/client/core/SessionUtilExternalBrowserTest.java @@ -5,10 +5,9 @@ package net.snowflake.client.core; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.when; @@ -32,8 +31,9 @@ import net.snowflake.common.core.ClientAuthnDTO; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; -import org.junit.Ignore; -import org.junit.Test; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -164,11 +164,13 @@ public void testSessionUtilExternalBrowser() throws Throwable { SessionUtilExternalBrowser sub = FakeSessionUtilExternalBrowser.createInstance(loginInput, false); sub.authenticate(); - assertThat("", sub.getToken(), equalTo(FakeSessionUtilExternalBrowser.MOCK_SAML_TOKEN)); + MatcherAssert.assertThat( + "", sub.getToken(), equalTo(FakeSessionUtilExternalBrowser.MOCK_SAML_TOKEN)); sub = FakeSessionUtilExternalBrowser.createInstance(loginInput, true); sub.authenticate(); - assertThat("", sub.getToken(), equalTo(FakeSessionUtilExternalBrowser.MOCK_SAML_TOKEN)); + MatcherAssert.assertThat( + "", sub.getToken(), equalTo(FakeSessionUtilExternalBrowser.MOCK_SAML_TOKEN)); } } @@ -200,7 +202,7 @@ public void testSessionUtilExternalBrowserFail() throws Throwable { sub.authenticate(); fail("should have failed with an exception."); } catch (SnowflakeSQLException ex) { - assertThat("Error is expected", ex.getErrorCode(), equalTo(123456)); + MatcherAssert.assertThat("Error is expected", ex.getErrorCode(), equalTo(123456)); } } } @@ -248,7 +250,7 @@ private SFLoginInput initMockLoginInput() { // Run this test manually to test disabling storing temporary credetials with external browser // auth. This is valid for versions after 3.18.0. @Test - @Ignore + @Disabled public void testEnableClientStoreTemporaryCredential() throws Exception { Map params = AbstractDriverIT.getConnectionParameters(); SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); @@ -270,7 +272,7 @@ public void testEnableClientStoreTemporaryCredential() throws Exception { // open a browser window for authentication, close the window, and you should get the expected // error message within the set timeout. Valid for driver versions after 3.18.0. @Test - @Ignore + @Disabled public void testExternalBrowserTimeout() throws Exception { Map params = AbstractDriverIT.getConnectionParameters(); SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); diff --git a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java index be6c03b01..57dde2a7b 100644 --- a/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java +++ b/src/test/java/net/snowflake/client/core/SessionUtilLatestIT.java @@ -5,8 +5,8 @@ package net.snowflake.client.core; import static net.snowflake.client.TestUtil.systemGetEnv; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; @@ -21,7 +21,7 @@ import java.util.Map.Entry; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.BaseJDBCTest; import net.snowflake.client.jdbc.ErrorCode; import net.snowflake.client.jdbc.SnowflakeSQLException; @@ -33,14 +33,14 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.MockedStatic.Verification; import org.mockito.Mockito; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class SessionUtilLatestIT extends BaseJDBCTest { /** @@ -50,7 +50,7 @@ public class SessionUtilLatestIT extends BaseJDBCTest { * @throws SFException * @throws SnowflakeSQLException */ - @Ignore + @Disabled @Test public void testJwtAuthTimeoutRetry() throws SFException, SnowflakeSQLException { final SFLoginInput loginInput = initMockLoginInput(); diff --git a/src/test/java/net/snowflake/client/core/SessionUtilTest.java b/src/test/java/net/snowflake/client/core/SessionUtilTest.java index cab5fb68f..86819dc5b 100644 --- a/src/test/java/net/snowflake/client/core/SessionUtilTest.java +++ b/src/test/java/net/snowflake/client/core/SessionUtilTest.java @@ -5,9 +5,9 @@ package net.snowflake.client.core; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.fasterxml.jackson.databind.node.BooleanNode; import java.io.IOException; @@ -20,9 +20,25 @@ import net.snowflake.client.jdbc.MockConnectionTest; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class SessionUtilTest { + private static String originalUrlValue; + private static String originalRetryUrlPattern; + + @BeforeAll + public static void saveStaticValues() { + originalUrlValue = SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_URL_VALUE; + originalRetryUrlPattern = SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN; + } + + @AfterAll + public static void restoreStaticValues() { + SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_URL_VALUE = originalUrlValue; + SFTrustManager.SF_OCSP_RESPONSE_CACHE_SERVER_RETRY_URL_PATTERN = originalRetryUrlPattern; + } /** Test isPrefixEqual */ @Test diff --git a/src/test/java/net/snowflake/client/core/SnowflakeMFACacheTest.java b/src/test/java/net/snowflake/client/core/SnowflakeMFACacheTest.java index f1f0a3e73..0524ab6b8 100644 --- a/src/test/java/net/snowflake/client/core/SnowflakeMFACacheTest.java +++ b/src/test/java/net/snowflake/client/core/SnowflakeMFACacheTest.java @@ -4,8 +4,9 @@ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -29,9 +30,8 @@ import net.snowflake.client.jdbc.SnowflakeSQLException; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.HttpPost; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; @@ -216,7 +216,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { // This connection would receive an exception and then should clean up the mfa cache try { Connection con3 = DriverManager.getConnection(url, prop); - Assert.fail(); + fail(); } catch (SnowflakeSQLException ex) { // An exception is forced to happen by mocking. Do nothing. } @@ -336,7 +336,7 @@ public void testUnavailableLocalSecureStorage() throws SQLException { // Run this test manually to test disabling the client request MFA token. Use an MFA // authentication enabled user. This is valid for versions after 3.18.0. @Test - @Ignore + @Disabled public void testEnableClientRequestMfaToken() throws SQLException { Map params = AbstractDriverIT.getConnectionParameters(); SnowflakeBasicDataSource ds = new SnowflakeBasicDataSource(); diff --git a/src/test/java/net/snowflake/client/core/SqlInputTimestampUtilTest.java b/src/test/java/net/snowflake/client/core/SqlInputTimestampUtilTest.java index 752229fc9..305f5563d 100644 --- a/src/test/java/net/snowflake/client/core/SqlInputTimestampUtilTest.java +++ b/src/test/java/net/snowflake/client/core/SqlInputTimestampUtilTest.java @@ -1,6 +1,6 @@ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.Timestamp; import java.time.LocalDateTime; @@ -8,12 +8,12 @@ import java.util.Map; import java.util.TimeZone; import net.snowflake.client.jdbc.SnowflakeUtil; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; -@Ignore +@Disabled public class SqlInputTimestampUtilTest { private static final String TIMESTAMP_IN_FORMAT_1 = "2021-12-22 09:43:44.000 +0100"; @@ -24,7 +24,7 @@ public class SqlInputTimestampUtilTest { private static SFBaseSession mockSession; - @BeforeClass + @BeforeAll public static void setup() { CONNECTION_PARAMS.put("TIMESTAMP_OUTPUT_FORMAT", "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM"); CONNECTION_PARAMS.put("TIMESTAMP_TZ_OUTPUT_FORMAT", "DY, DD MON YYYY HH24:MI:SS TZHTZM"); diff --git a/src/test/java/net/snowflake/client/core/StmtUtilTest.java b/src/test/java/net/snowflake/client/core/StmtUtilTest.java index 75daa9a03..7075416e4 100644 --- a/src/test/java/net/snowflake/client/core/StmtUtilTest.java +++ b/src/test/java/net/snowflake/client/core/StmtUtilTest.java @@ -13,17 +13,17 @@ import java.util.Map.Entry; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.StmtUtil.StmtInput; import net.snowflake.client.jdbc.BaseJDBCTest; import org.apache.http.Header; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.MockedStatic.Verification; import org.mockito.Mockito; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class StmtUtilTest extends BaseJDBCTest { /** SNOW-862760 Verify that additional headers are added to request */ diff --git a/src/test/java/net/snowflake/client/core/URLUtilTest.java b/src/test/java/net/snowflake/client/core/URLUtilTest.java index b61324eee..d2903b2c5 100644 --- a/src/test/java/net/snowflake/client/core/URLUtilTest.java +++ b/src/test/java/net/snowflake/client/core/URLUtilTest.java @@ -3,11 +3,11 @@ */ package net.snowflake.client.core; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class URLUtilTest { diff --git a/src/test/java/net/snowflake/client/core/arrow/ArrowResultUtilTest.java b/src/test/java/net/snowflake/client/core/arrow/ArrowResultUtilTest.java index 75b24cc07..4dc6855b1 100644 --- a/src/test/java/net/snowflake/client/core/arrow/ArrowResultUtilTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/ArrowResultUtilTest.java @@ -4,43 +4,42 @@ package net.snowflake.client.core.arrow; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; import java.util.Random; import java.util.TimeZone; +import java.util.stream.Stream; import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; import net.snowflake.client.core.SFSession; -import org.junit.After; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import net.snowflake.client.providers.TimezoneProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class ArrowResultUtilTest { - // test on multiple time zones - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, {"America/Los_Angeles"}, {"America/New_York"}, {"Asia/Singapore"}, {"MEZ"}, - }; - } - - @After - public void clearTimeZone() { + @AfterAll + public static void clearTimeZone() { System.clearProperty("user.timezone"); } - public ArrowResultUtilTest(String tz) { - System.setProperty("user.timezone", tz); + public static void setTimeZone(String string) { + System.setProperty("user.timezone", string); } - @Test - @Ignore + @ParameterizedTest(name = "Timezone = {0}") + @ArgumentsSource(TimezoneProvider.class) + @Disabled /** This is to show we can have 30X improvement using new API */ - public void testGetDatePerformance() throws SFException { + public void testGetDatePerformance(String timezone) throws SFException { + setTimeZone(timezone); Random random = new Random(); int dateBound = 50000; int times = 100000; @@ -71,17 +70,43 @@ public void testGetDatePerformance() throws SFException { System.out.println(duration1 + " " + duration2 + " " + duration3); } - @Test - public void testToJavaTimestamp() { + private static class testCasesProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + List timezones = + new ArrayList() { + { + add("UTC"); + add("America/Los_Angeles"); + add("America/New_York"); + add("Asia/Singapore"); + add("MEZ"); + } + }; + + long[] cases = {-1123456789, -123456789, 123456789, 123123456789L, -123123456789L}; + long[] millisecs = {-1124, -124, 123, 123123, -123124}; + int[] nanos = {876543211, 876543211, 123456789, 123456789, 876543211}; + + List args = new ArrayList<>(); + for (String timezone : timezones) { + for (int i = 0; i < cases.length; i++) { + args.add(Arguments.of(timezone, cases[i], millisecs[i], nanos[i])); + } + } + + return args.stream(); + } + } + + @ParameterizedTest + @ArgumentsSource(testCasesProvider.class) + public void testToJavaTimestamp(String timezone, long cas, long millisecs, int nanos) { // ex: -1.123456789, -0.123456789, 0.123456789, 123.123456789, -123.123456789 - long[] cases = {-1123456789, -123456789, 123456789, 123123456789l, -123123456789l}; - long[] millisecs = {-1124, -124, 123, 123123, -123124}; - int[] nanos = {876543211, 876543211, 123456789, 123456789, 876543211}; + setTimeZone(timezone); int scale = 9; - for (int i = 0; i < cases.length; i++) { - Timestamp ts = ArrowResultUtil.toJavaTimestamp(cases[i], scale); - assertEquals(millisecs[i], ts.getTime()); - assertEquals(nanos[i], ts.getNanos()); - } + Timestamp ts = ArrowResultUtil.toJavaTimestamp(cas, scale); + assertEquals(millisecs, ts.getTime()); + assertEquals(nanos, ts.getNanos()); } } diff --git a/src/test/java/net/snowflake/client/core/arrow/BaseConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BaseConverterTest.java index e669ac006..a738676fb 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BaseConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BaseConverterTest.java @@ -10,9 +10,9 @@ import net.snowflake.client.jdbc.ErrorCode; import net.snowflake.common.core.SFBinaryFormat; import net.snowflake.common.core.SnowflakeDateTimeFormat; -import org.junit.After; -import org.junit.Assume; -import org.junit.Before; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; public class BaseConverterTest implements DataConversionContext { private SnowflakeDateTimeFormat dateTimeFormat = @@ -30,16 +30,16 @@ public class BaseConverterTest implements DataConversionContext { private boolean honorClientTZForTimestampNTZ; protected final int invalidConversionErrorCode = ErrorCode.INVALID_VALUE_CONVERT.getMessageCode(); - @After + @AfterEach public void clearTimeZone() { System.clearProperty("user.timezone"); } - @Before + @BeforeEach public void assumeLittleEndian() { - Assume.assumeTrue( - "Arrow doesn't support cross endianness", - ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)); + Assumptions.assumeTrue( + ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN), + "Arrow doesn't support cross endianness"); } @Override diff --git a/src/test/java/net/snowflake/client/core/arrow/BigIntToFixedConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BigIntToFixedConverterTest.java index 74eabad29..230288f4a 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BigIntToFixedConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BigIntToFixedConverterTest.java @@ -7,8 +7,8 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.nio.ByteBuffer; @@ -27,7 +27,7 @@ import org.apache.arrow.vector.BigIntVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class BigIntToFixedConverterTest extends BaseConverterTest { /** allocator for arrow */ diff --git a/src/test/java/net/snowflake/client/core/arrow/BigIntToTimeConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BigIntToTimeConverterTest.java index 9248440bb..b2be8f8cd 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BigIntToTimeConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BigIntToTimeConverterTest.java @@ -7,8 +7,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Time; import java.util.HashMap; @@ -20,32 +20,24 @@ import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; import net.snowflake.client.core.SFSession; +import net.snowflake.client.providers.TimezoneProvider; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; import org.apache.arrow.vector.BigIntVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class BigIntToTimeConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; + public void setTimezone(String tz) { + System.setProperty("user.timezone", tz); } - public BigIntToTimeConverterTest(String tz) { - System.setProperty("user.timezone", tz); + @AfterAll + public static void clearTimezone() { + System.clearProperty("user.timezone"); } /** allocator for arrow */ @@ -55,8 +47,10 @@ public BigIntToTimeConverterTest(String tz) { private int scale = 9; - @Test - public void testTime() throws SFException { + @ParameterizedTest(name = "{0}") + @ArgumentsSource(TimezoneProvider.class) + public void testTime(String tz) throws SFException { + setTimezone(tz); // test old and new dates long[] testTimesInt64 = {12345678000000L}; diff --git a/src/test/java/net/snowflake/client/core/arrow/BigIntToTimestampLTZConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BigIntToTimestampLTZConverterTest.java index 26fdbc052..298bf443b 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BigIntToTimestampLTZConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BigIntToTimestampLTZConverterTest.java @@ -8,8 +8,8 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Date; import java.sql.Time; @@ -23,34 +23,17 @@ import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; import net.snowflake.client.jdbc.SnowflakeUtil; +import net.snowflake.client.providers.TimezoneProvider; import net.snowflake.common.core.SFTimestamp; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; import org.apache.arrow.vector.BigIntVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class BigIntToTimestampLTZConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; - } - - public BigIntToTimestampLTZConverterTest(String tz) { - System.setProperty("user.timezone", tz); - } /** allocator for arrow */ private BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); @@ -59,8 +42,10 @@ public BigIntToTimestampLTZConverterTest(String tz) { private int oldScale = 9; - @Test - public void testTimestampLTZ() throws SFException { + @ParameterizedTest + @ArgumentsSource(TimezoneProvider.class) + public void testTimestampLTZ(String timezone) throws SFException { + System.setProperty("user.timezone", timezone); // test old and new dates long[] testTimestampsInt64 = { 1546391837, diff --git a/src/test/java/net/snowflake/client/core/arrow/BigIntToTimestampNTZConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BigIntToTimestampNTZConverterTest.java index df4370641..6f2c0420d 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BigIntToTimestampNTZConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BigIntToTimestampNTZConverterTest.java @@ -4,18 +4,21 @@ package net.snowflake.client.core.arrow; +import static net.snowflake.client.providers.ProvidersUtil.cartesianProduct; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; @@ -23,33 +26,32 @@ import net.snowflake.client.TestUtil; import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; +import net.snowflake.client.providers.SnowflakeArgumentsProvider; +import net.snowflake.client.providers.TimezoneProvider; import net.snowflake.common.core.SFTimestamp; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; import org.apache.arrow.vector.BigIntVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class BigIntToTimestampNTZConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; + static class FlagProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return Arrays.asList(Arguments.of(true), Arguments.of(false)); + } } - public BigIntToTimestampNTZConverterTest(String tz) { - System.setProperty("user.timezone", tz); + static class DataProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return cartesianProduct(context, new TimezoneProvider(), new FlagProvider()); + } } /** allocator for arrow */ @@ -59,25 +61,18 @@ public BigIntToTimestampNTZConverterTest(String tz) { private int oldScale = 9; - @Test - public void testHonorClientTZForTimestampNTZDisabled() throws SFException { - this.setHonorClientTZForTimestampNTZ(false); - testTimestampNTZ(); - } - - @Test - public void testHonorClientTZForTimestampNTZEnabled() throws SFException { - this.setHonorClientTZForTimestampNTZ(true); - testTimestampNTZ(); - } - - @Test - public void testWithNullTimezone() throws SFException { + @ParameterizedTest + @ArgumentsSource(TimezoneProvider.class) + public void testWithNullTimezone(String tz) throws SFException { + System.setProperty("user.timezone", tz); testTimestampNTZ(null); } - @Test - public void testTimestampNTZ() throws SFException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampNTZ(String tz, boolean flag) throws SFException { + this.setHonorClientTZForTimestampNTZ(flag); + System.setProperty("user.timezone", tz); testTimestampNTZ(TimeZone.getDefault()); } diff --git a/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java index e5091d6fc..c30bbd0e6 100644 --- a/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/BitToBooleanConverterTest.java @@ -3,8 +3,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.HashMap; @@ -19,7 +19,7 @@ import org.apache.arrow.vector.BitVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class BitToBooleanConverterTest extends BaseConverterTest { /** allocator for arrow */ diff --git a/src/test/java/net/snowflake/client/core/arrow/DateConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/DateConverterTest.java index b63ae9a2d..6857394fc 100644 --- a/src/test/java/net/snowflake/client/core/arrow/DateConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/DateConverterTest.java @@ -3,8 +3,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Date; import java.util.Arrays; @@ -18,33 +18,20 @@ import net.snowflake.client.TestUtil; import net.snowflake.client.core.SFException; import net.snowflake.client.core.json.DateTimeConverter; +import net.snowflake.client.providers.TimezoneProvider; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; import org.apache.arrow.vector.DateDayVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class DateConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; - } - public DateConverterTest(String tz) { + private static void setTimeZone(String tz) { System.setProperty("user.timezone", tz); } @@ -78,26 +65,28 @@ public DateConverterTest(String tz) { put("America/New_York", Arrays.asList("2016-04-20", -4)); put("Pacific/Honolulu", Arrays.asList("2016-04-20", -10)); put("Asia/Singapore", Arrays.asList("2016-04-19", 8)); - put("MEZ", Arrays.asList("2016-04-20", 0)); - put("MESZ", Arrays.asList("2016-04-20", 0)); + put("CET", Arrays.asList("2016-04-19", 2)); // because of daylight savings + put("GMT+0200", Arrays.asList("2016-04-19", 2)); } }; public static final int MILLIS_IN_ONE_HOUR = 3600000; private TimeZone defaultTimeZone; - @Before + @BeforeEach public void getDefaultTimeZone() { this.defaultTimeZone = TimeZone.getDefault(); } - @After + @AfterEach public void restoreDefaultTimeZone() { TimeZone.setDefault(defaultTimeZone); } - @Test - public void testDate() throws SFException { + @ParameterizedTest + @ArgumentsSource(TimezoneProvider.class) + public void testDate(String tz) throws SFException { + setTimeZone(tz); Map customFieldMeta = new HashMap<>(); customFieldMeta.put("logicalType", "DATE"); Set nullValIndex = new HashSet<>(); @@ -153,8 +142,10 @@ public void testDate() throws SFException { vector.clear(); } - @Test - public void testRandomDates() throws SFException { + @ParameterizedTest + @ArgumentsSource(TimezoneProvider.class) + public void testRandomDates(String tz) throws SFException { + setTimeZone(tz); int dateBound = 50000; int rowCount = 50000; Map customFieldMeta = new HashMap<>(); @@ -196,8 +187,10 @@ public void testRandomDates() throws SFException { } } - @Test - public void testTimezoneDates() throws SFException { + @ParameterizedTest + @ArgumentsSource(TimezoneProvider.class) + public void testTimezoneDates(String tz) throws SFException { + setTimeZone(tz); int testDay = 16911; Map customFieldMeta = new HashMap<>(); customFieldMeta.put("logicalType", "DATE"); @@ -211,7 +204,6 @@ public void testTimezoneDates() throws SFException { // Test JDBC_FORMAT_DATE_WITH_TIMEZONE=TRUE with different session timezones TimeZone.setDefault(TimeZone.getTimeZone("UTC")); - String tz = System.getProperty("user.timezone"); ArrowVectorConverter converter = new DateConverter(vector, 0, this, true); converter.setUseSessionTimezone(true); converter.setSessionTimeZone(TimeZone.getTimeZone(tz)); diff --git a/src/test/java/net/snowflake/client/core/arrow/DoubleToRealConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/DoubleToRealConverterTest.java index b242a2be8..718daa69c 100644 --- a/src/test/java/net/snowflake/client/core/arrow/DoubleToRealConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/DoubleToRealConverterTest.java @@ -6,8 +6,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -25,7 +25,7 @@ import org.apache.arrow.vector.Float8Vector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class DoubleToRealConverterTest extends BaseConverterTest { /** allocator for arrow */ diff --git a/src/test/java/net/snowflake/client/core/arrow/IntToFixedConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/IntToFixedConverterTest.java index c11d8275d..fc4db1875 100644 --- a/src/test/java/net/snowflake/client/core/arrow/IntToFixedConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/IntToFixedConverterTest.java @@ -7,9 +7,9 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.nio.ByteBuffer; @@ -29,7 +29,7 @@ import org.apache.arrow.vector.IntVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class IntToFixedConverterTest extends BaseConverterTest { /** allocator for arrow */ diff --git a/src/test/java/net/snowflake/client/core/arrow/IntToTimeConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/IntToTimeConverterTest.java index 92c560db3..1f4bd955f 100644 --- a/src/test/java/net/snowflake/client/core/arrow/IntToTimeConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/IntToTimeConverterTest.java @@ -8,10 +8,9 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -import java.nio.ByteBuffer; import java.sql.Time; import java.util.HashMap; import java.util.HashSet; @@ -22,46 +21,31 @@ import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; import net.snowflake.client.core.SFSession; +import net.snowflake.client.providers.TimezoneProvider; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; import org.apache.arrow.vector.IntVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class IntToTimeConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; - } - - private ByteBuffer bb; - - public IntToTimeConverterTest(String tz) { - System.setProperty("user.timezone", tz); - this.setScale(scale); - } - /** allocator for arrow */ private BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); private Random random = new Random(); + public IntToTimeConverterTest() { + this.setScale(scale); + } + private int scale = 3; - @Test - public void testTime() throws SFException { + @ParameterizedTest + @ArgumentsSource(TimezoneProvider.class) + public void testTime(String timezone) throws SFException { + System.setProperty("user.timezone", timezone); // test old and new dates int[] testTimesInt = {12345678}; diff --git a/src/test/java/net/snowflake/client/core/arrow/SmallIntToFixedConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/SmallIntToFixedConverterTest.java index 5513a420b..d37b005b1 100644 --- a/src/test/java/net/snowflake/client/core/arrow/SmallIntToFixedConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/SmallIntToFixedConverterTest.java @@ -7,9 +7,9 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.nio.ByteBuffer; @@ -29,7 +29,7 @@ import org.apache.arrow.vector.SmallIntVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SmallIntToFixedConverterTest extends BaseConverterTest { /** allocator for arrow */ diff --git a/src/test/java/net/snowflake/client/core/arrow/ThreeFieldStructToTimestampTZConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/ThreeFieldStructToTimestampTZConverterTest.java index 10721fbc1..09cd4a587 100644 --- a/src/test/java/net/snowflake/client/core/arrow/ThreeFieldStructToTimestampTZConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/ThreeFieldStructToTimestampTZConverterTest.java @@ -4,15 +4,17 @@ package net.snowflake.client.core.arrow; +import static java.util.stream.Stream.concat; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -20,6 +22,7 @@ import java.util.Map; import java.util.Random; import java.util.Set; +import java.util.stream.Stream; import net.snowflake.client.TestUtil; import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; @@ -33,29 +36,73 @@ import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.Field; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class ThreeFieldStructToTimestampTZConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; + private static class TimezoneProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + List timezones = + new ArrayList() { + { + add("America/Los_Angeles"); + add("America/New_York"); + add("Pacific/Honolulu"); + add("Asia/Singapore"); + add("MESZ"); + add("MEZ"); + add("UTC"); + } + }; + + Stream args = Stream.empty(); + + for (String timezone : timezones) { + args = + concat( + args, + Stream.of( + Arguments.argumentSet( + timezone, + timezone, + new long[] {1546391837, 1546391837, 0, 123, -12346, -12345}, + new int[] {0, 10, 100, 456, 876543211, 0}, + new int[] {960, 1440, 960, 960, 1440, 1440}, + new String[] { + "1546391837.000000000 960", + "1546391837.000000010 1440", + "0.000000100 960", + "123.000000456 960", + "-12345.123456789 1440", + "-12345.000000000 1440" + }), + Arguments.argumentSet( + timezone + " Overflow", + timezone, + new long[] {1546391837}, + new int[] {0}, + new int[] {960}, + new String[] {"1546391837.000000000 960"}))); + } + + return args; + } } - public ThreeFieldStructToTimestampTZConverterTest(String tz) { + private static void setTimezone(String tz) { System.setProperty("user.timezone", tz); } + @AfterAll + public static void clearTimezone() { + System.clearProperty("user.timezone"); + } + /** allocator for arrow */ private BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); @@ -63,42 +110,16 @@ public ThreeFieldStructToTimestampTZConverterTest(String tz) { private int oldScale = 9; - @Test - public void simpleTest() throws SFException { - // test old and new dates - long[] testSecondsInt64 = {1546391837, 1546391837, 0, 123, -12346, -12345}; - - int[] testNanos = {0, 10, 100, 456, 876543211, 0}; - - int[] testTimeZoneIndices = {960, 1440, 960, 960, 1440, 1440}; - - String[] testTimesJson = { - "1546391837.000000000 960", - "1546391837.000000010 1440", - "0.000000100 960", - "123.000000456 960", - "-12345.123456789 1440", - "-12345.000000000 1440" - }; - testTimestampTZ(testSecondsInt64, testNanos, testTimeZoneIndices, testTimesJson); - } - - @Test - public void timestampOverflowTest() throws SFException { - // test old and new dates - long[] testSecondsInt64 = {1546391837}; - - int[] testNanos = {0}; - - int[] testTimeZoneIndices = {960}; - - String[] testTimesJson = {"1546391837.000000000 960"}; - testTimestampTZ(testSecondsInt64, testNanos, testTimeZoneIndices, testTimesJson); - } - + @ParameterizedTest + @ArgumentsSource(TimezoneProvider.class) public void testTimestampTZ( - long[] testSecondsInt64, int[] testNanos, int[] testTimeZoneIndices, String[] testTimesJson) + String tz, + long[] testSecondsInt64, + int[] testNanos, + int[] testTimeZoneIndices, + String[] testTimesJson) throws SFException { + setTimezone(tz); Map customFieldMeta = new HashMap<>(); customFieldMeta.put("logicalType", "TIMESTAMP"); diff --git a/src/test/java/net/snowflake/client/core/arrow/TinyIntToFixedConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/TinyIntToFixedConverterTest.java index 8000ec885..8a1e9b359 100644 --- a/src/test/java/net/snowflake/client/core/arrow/TinyIntToFixedConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/TinyIntToFixedConverterTest.java @@ -6,9 +6,9 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.util.ArrayList; @@ -27,7 +27,7 @@ import org.apache.arrow.vector.TinyIntVector; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class TinyIntToFixedConverterTest extends BaseConverterTest { /** allocator for arrow */ diff --git a/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampLTZConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampLTZConverterTest.java index 8ce93fb6a..4fd4f07f3 100644 --- a/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampLTZConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampLTZConverterTest.java @@ -4,15 +4,17 @@ package net.snowflake.client.core.arrow; +import static java.util.stream.Stream.concat; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -20,6 +22,7 @@ import java.util.Map; import java.util.Random; import java.util.Set; +import java.util.stream.Stream; import net.snowflake.client.TestUtil; import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; @@ -33,29 +36,70 @@ import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.Field; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class TwoFieldStructToTimestampLTZConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; + + static class DataProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + List timezones = + new ArrayList() { + { + add("America/Los_Angeles"); + add("America/New_York"); + add("Pacific/Honolulu"); + add("Asia/Singapore"); + add("MESZ"); + add("MEZ"); + add("UTC"); + } + }; + + Stream args = Stream.empty(); + + for (String timezone : timezones) { + args = + concat( + args, + Stream.of( + Arguments.argumentSet( + timezone, + timezone, + new long[] {1546391837, 0, -1546391838, -1546391838, -1546391838}, + new int[] {0, 1, 999999990, 876543211, 1}, + new String[] { + "1546391837.000000000", + "0.000000001", + "-1546391837.000000010", + "-1546391837.123456789", + "-1546391837.999999999" + }), + Arguments.argumentSet( + timezone + " Overflow", + timezone, + new long[] {154639183700000L}, + new int[] {0}, + new String[] {"154639183700000.000000000"}))); + } + return args; + } } - public TwoFieldStructToTimestampLTZConverterTest(String tz) { + private static void setTimezone(String tz) { System.setProperty("user.timezone", tz); } + @AfterAll + public static void clearTimezone() { + System.clearProperty("user.timezone"); + } + /** allocator for arrow */ private BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); @@ -63,37 +107,13 @@ public TwoFieldStructToTimestampLTZConverterTest(String tz) { private int oldScale = 9; - @Test - public void simpleTests() throws SFException { - // test old and new dates - long[] testSecondsInt64 = {1546391837, 0, -1546391838, -1546391838, -1546391838}; - - int[] testNanoSecs = {0, 1, 999999990, 876543211, 1}; - - String[] testTimesJson = { - "1546391837.000000000", - "0.000000001", - "-1546391837.000000010", - "-1546391837.123456789", - "-1546391837.999999999" - }; - testTimestampLTZ(testSecondsInt64, testNanoSecs, testTimesJson); - } - - @Test - public void timestampOverflowTests() throws SFException { - // test old and new dates - long[] testSecondsInt64 = {154639183700000l}; - - int[] testNanoSecs = {0}; - - String[] testTimesJson = {"154639183700000.000000000"}; - testTimestampLTZ(testSecondsInt64, testNanoSecs, testTimesJson); - } - - public void testTimestampLTZ(long[] testSecondsInt64, int[] testNanoSecs, String[] testTimesJson) + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampLTZ( + String timezone, long[] testSecondsInt64, int[] testNanoSecs, String[] testTimesJson) throws SFException { + setTimezone(timezone); Map customFieldMeta = new HashMap<>(); customFieldMeta.put("logicalType", "TIMESTAMP"); Set nullValIndex = new HashSet<>(); diff --git a/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampNTZConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampNTZConverterTest.java index 2b5bf0e16..3b84176e4 100644 --- a/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampNTZConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampNTZConverterTest.java @@ -4,15 +4,17 @@ package net.snowflake.client.core.arrow; +import static java.util.stream.Stream.concat; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -21,6 +23,7 @@ import java.util.Random; import java.util.Set; import java.util.TimeZone; +import java.util.stream.Stream; import net.snowflake.client.TestUtil; import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; @@ -33,29 +36,24 @@ import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.Field; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class TwoFieldStructToTimestampNTZConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; - } - public TwoFieldStructToTimestampNTZConverterTest(String tz) { + private static void setTimezone(String tz) { System.setProperty("user.timezone", tz); } + @AfterAll + public static void clearTimezone() { + System.clearProperty("user.timezone"); + } + /** allocator for arrow */ private BufferAllocator allocator = new RootAllocator(Long.MAX_VALUE); @@ -63,56 +61,80 @@ public TwoFieldStructToTimestampNTZConverterTest(String tz) { private int oldScale = 9; - @Test - public void timestampOverflowTest() throws SFException { - // test old and new dates - long[] testSecondsInt64 = {154639183700000l}; - - int[] testNanoSecs = {0}; - - String[] testTimesJson = {"154639183700000.000000000"}; - this.setHonorClientTZForTimestampNTZ(false); - testTimestampNTZ(testSecondsInt64, testNanoSecs, testTimesJson); - } - - @Test - public void testHonorClientTZForTimestampNTZDisabled() throws SFException { - // test old and new dates - long[] testSecondsInt64 = {1546391837, 0, -1546391838, -1546391838, -1546391838}; - - int[] testNanoSecs = {0, 1, 999999990, 876543211, 1}; - - String[] testTimesJson = { - "1546391837.000000000", - "0.000000001", - "-1546391837.000000010", - "-1546391837.123456789", - "-1546391837.999999999" - }; - this.setHonorClientTZForTimestampNTZ(false); - testTimestampNTZ(testSecondsInt64, testNanoSecs, testTimesJson); - } + static class DataProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + List timezones = + new ArrayList() { + { + add("America/Los_Angeles"); + add("America/New_York"); + add("Pacific/Honolulu"); + add("Asia/Singapore"); + add("MESZ"); + add("MEZ"); + add("UTC"); + } + }; + + Stream args = Stream.empty(); + + for (String timezone : timezones) { + args = + concat( + args, + Stream.of( + Arguments.argumentSet( + timezone + " Overflow", + timezone, + false, + new long[] {154639183700000L}, + new int[] {0}, + new String[] {"154639183700000.000000000"}), + Arguments.argumentSet( + timezone + " HonorClientTZForTimestampNTZ Disabled", + timezone, + false, + new long[] {1546391837, 0, -1546391838, -1546391838, -1546391838}, + new int[] {0, 1, 999999990, 876543211, 1}, + new String[] { + "1546391837.000000000", + "0.000000001", + "-1546391837.000000010", + "-1546391837.123456789", + "-1546391837.999999999" + }), + Arguments.argumentSet( + timezone + " HonorClientTZForTimestampNTZ Enabled", + timezone, + true, + new long[] {1546391837, 1546391837, 1546391837, 1546391837, 1546391837}, + new int[] {0, 1, 10, 100, 999999999}, + new String[] { + "1546391837.000000000", + "1546391837.000000001", + "1546391837.000000010", + "1546391837.000000100", + "1546391837.999999999" + }))); + } - @Test - public void testHonorClientTZForTimestampNTZEnabled() throws SFException { - // test old and new dates - long[] testSecondsInt64 = {1546391837, 1546391837, 1546391837, 1546391837, 1546391837}; - - int[] testNanoSecs = {0, 1, 10, 100, 999999999}; - - String[] testTimesJson = { - "1546391837.000000000", - "1546391837.000000001", - "1546391837.000000010", - "1546391837.000000100", - "1546391837.999999999" - }; - this.setHonorClientTZForTimestampNTZ(true); - testTimestampNTZ(testSecondsInt64, testNanoSecs, testTimesJson); + return args; + } } - public void testTimestampNTZ(long[] testSecondsInt64, int[] testNanoSecs, String[] testTimesJson) + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampNTZ( + String timezone, + boolean honorClientTZForTimestampNTZ, + long[] testSecondsInt64, + int[] testNanoSecs, + String[] testTimesJson) throws SFException { + this.setHonorClientTZForTimestampNTZ(honorClientTZForTimestampNTZ); + setTimezone(timezone); Map customFieldMeta = new HashMap<>(); customFieldMeta.put("logicalType", "TIMESTAMP"); diff --git a/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampTZConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampTZConverterTest.java index 742b82751..767938d06 100644 --- a/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampTZConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/TwoFieldStructToTimestampTZConverterTest.java @@ -7,8 +7,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Date; import java.sql.Time; @@ -24,6 +24,7 @@ import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFException; import net.snowflake.client.jdbc.SnowflakeUtil; +import net.snowflake.client.providers.TimezoneProvider; import net.snowflake.common.core.SFTimestamp; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; @@ -33,27 +34,18 @@ import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.Field; import org.apache.arrow.vector.types.pojo.FieldType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class TwoFieldStructToTimestampTZConverterTest extends BaseConverterTest { - @Parameterized.Parameters - public static Object[][] data() { - return new Object[][] { - {"UTC"}, - {"America/Los_Angeles"}, - {"America/New_York"}, - {"Pacific/Honolulu"}, - {"Asia/Singapore"}, - {"MEZ"}, - {"MESZ"} - }; + public static void setTimezone(String tz) { + System.setProperty("user.timezone", tz); } - public TwoFieldStructToTimestampTZConverterTest(String tz) { - System.setProperty("user.timezone", tz); + @AfterAll + public static void clearTimezone() { + System.clearProperty("user.timezone"); } /** allocator for arrow */ @@ -63,8 +55,10 @@ public TwoFieldStructToTimestampTZConverterTest(String tz) { private int oldScale = 9; - @Test - public void testTimestampTZ() throws SFException { + @ParameterizedTest + @ArgumentsSource(TimezoneProvider.class) + public void testTimestampTZ(String tz) throws SFException { + setTimezone(tz); // test old and new dates long[] testEpochesInt64 = {1546391837, 1546391837, 0, 123, -12345, -12345678}; diff --git a/src/test/java/net/snowflake/client/core/arrow/VarBinaryToBinaryConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/VarBinaryToBinaryConverterTest.java index b6ea49f05..231df247c 100644 --- a/src/test/java/net/snowflake/client/core/arrow/VarBinaryToBinaryConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/VarBinaryToBinaryConverterTest.java @@ -6,8 +6,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Base64; @@ -25,7 +25,7 @@ import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class VarBinaryToBinaryConverterTest extends BaseConverterTest { /** allocator for arrow */ diff --git a/src/test/java/net/snowflake/client/core/arrow/VarCharConverterTest.java b/src/test/java/net/snowflake/client/core/arrow/VarCharConverterTest.java index 6569c0309..692e171d0 100644 --- a/src/test/java/net/snowflake/client/core/arrow/VarCharConverterTest.java +++ b/src/test/java/net/snowflake/client/core/arrow/VarCharConverterTest.java @@ -6,8 +6,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.charset.StandardCharsets; import java.sql.Date; @@ -27,7 +27,7 @@ import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.FieldType; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class VarCharConverterTest extends BaseConverterTest { /** allocator for arrow */ diff --git a/src/test/java/net/snowflake/client/core/bind/BindExceptionTest.java b/src/test/java/net/snowflake/client/core/bind/BindExceptionTest.java index f3ae88eee..d50118ff8 100644 --- a/src/test/java/net/snowflake/client/core/bind/BindExceptionTest.java +++ b/src/test/java/net/snowflake/client/core/bind/BindExceptionTest.java @@ -1,9 +1,9 @@ package net.snowflake.client.core.bind; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import net.snowflake.client.jdbc.telemetry.TelemetryField; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class BindExceptionTest { diff --git a/src/test/java/net/snowflake/client/core/json/BooleanConverterTest.java b/src/test/java/net/snowflake/client/core/json/BooleanConverterTest.java index 2162d651a..292c3862f 100644 --- a/src/test/java/net/snowflake/client/core/json/BooleanConverterTest.java +++ b/src/test/java/net/snowflake/client/core/json/BooleanConverterTest.java @@ -2,10 +2,11 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.sql.Types; import net.snowflake.client.core.SFException; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class BooleanConverterTest { private final BooleanConverter booleanConverter = new BooleanConverter(); @@ -44,8 +45,8 @@ public void testConvertString() throws SFException { assertThat(booleanConverter.getBoolean("FALSE", Types.CHAR), equalTo(false)); } - @Test(expected = SFException.class) - public void testConvertOtherType() throws SFException { - booleanConverter.getBoolean("1", Types.BINARY); + @Test + public void testConvertOtherType() { + assertThrows(SFException.class, () -> booleanConverter.getBoolean("1", Types.BINARY)); } } diff --git a/src/test/java/net/snowflake/client/core/json/BytesConverterTest.java b/src/test/java/net/snowflake/client/core/json/BytesConverterTest.java index 47e898486..3f7956ad7 100644 --- a/src/test/java/net/snowflake/client/core/json/BytesConverterTest.java +++ b/src/test/java/net/snowflake/client/core/json/BytesConverterTest.java @@ -1,6 +1,6 @@ package net.snowflake.client.core.json; -import static org.junit.Assert.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -8,7 +8,7 @@ import net.snowflake.client.core.SFException; import net.snowflake.client.core.SFSession; import org.apache.arrow.vector.Float8Vector; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class BytesConverterTest { private final Converters converters = diff --git a/src/test/java/net/snowflake/client/core/json/DateTimeConverterTest.java b/src/test/java/net/snowflake/client/core/json/DateTimeConverterTest.java index 985264f3e..21fe82043 100644 --- a/src/test/java/net/snowflake/client/core/json/DateTimeConverterTest.java +++ b/src/test/java/net/snowflake/client/core/json/DateTimeConverterTest.java @@ -1,7 +1,7 @@ package net.snowflake.client.core.json; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.sql.Date; import java.sql.Time; @@ -15,7 +15,7 @@ import net.snowflake.client.core.SFException; import net.snowflake.client.core.SFSession; import net.snowflake.client.jdbc.SnowflakeUtil; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class DateTimeConverterTest { private final TimeZone honoluluTimeZone = diff --git a/src/test/java/net/snowflake/client/core/json/NumberConverterTest.java b/src/test/java/net/snowflake/client/core/json/NumberConverterTest.java index c37573b72..41f6460b4 100644 --- a/src/test/java/net/snowflake/client/core/json/NumberConverterTest.java +++ b/src/test/java/net/snowflake/client/core/json/NumberConverterTest.java @@ -6,7 +6,7 @@ import java.math.BigDecimal; import java.sql.Types; import net.snowflake.client.core.SFException; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class NumberConverterTest { private final NumberConverter numberConverter = new NumberConverter(); diff --git a/src/test/java/net/snowflake/client/core/json/StringConverterTest.java b/src/test/java/net/snowflake/client/core/json/StringConverterTest.java index 5fe3dd2cb..d2ddb3eee 100644 --- a/src/test/java/net/snowflake/client/core/json/StringConverterTest.java +++ b/src/test/java/net/snowflake/client/core/json/StringConverterTest.java @@ -1,6 +1,6 @@ package net.snowflake.client.core.json; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import java.sql.Types; @@ -12,8 +12,8 @@ import net.snowflake.client.jdbc.SnowflakeUtil; import net.snowflake.common.core.SFBinaryFormat; import net.snowflake.common.core.SnowflakeDateTimeFormat; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class StringConverterTest { private final TimeZone honoluluTimeZone = @@ -24,7 +24,7 @@ public class StringConverterTest { private StringConverter stringConverter; - @Before + @BeforeEach public void init() { SnowflakeDateTimeFormat timestampNTZFormatter = SnowflakeDateTimeFormat.fromSqlFormat("YYYY-MM-DD HH24:MI:SS.FF3"); diff --git a/src/test/java/net/snowflake/client/jdbc/ArrowResultChunkTest.java b/src/test/java/net/snowflake/client/jdbc/ArrowResultChunkTest.java index 2c37ddf5d..59e2b30a2 100644 --- a/src/test/java/net/snowflake/client/jdbc/ArrowResultChunkTest.java +++ b/src/test/java/net/snowflake/client/jdbc/ArrowResultChunkTest.java @@ -6,7 +6,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class ArrowResultChunkTest { @Test diff --git a/src/test/java/net/snowflake/client/jdbc/BaseJDBCTest.java b/src/test/java/net/snowflake/client/jdbc/BaseJDBCTest.java index a326dea12..c1abedf68 100644 --- a/src/test/java/net/snowflake/client/jdbc/BaseJDBCTest.java +++ b/src/test/java/net/snowflake/client/jdbc/BaseJDBCTest.java @@ -3,9 +3,9 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.io.InputStream; diff --git a/src/test/java/net/snowflake/client/jdbc/BaseJDBCWithSharedConnectionIT.java b/src/test/java/net/snowflake/client/jdbc/BaseJDBCWithSharedConnectionIT.java index 5602bffca..f05d45afe 100644 --- a/src/test/java/net/snowflake/client/jdbc/BaseJDBCWithSharedConnectionIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BaseJDBCWithSharedConnectionIT.java @@ -2,22 +2,29 @@ import java.sql.Connection; import java.sql.SQLException; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import java.sql.Statement; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; public class BaseJDBCWithSharedConnectionIT extends BaseJDBCTest { protected static Connection connection; - @BeforeClass + @BeforeAll public static void setUpConnection() throws SQLException { connection = getConnection(); } - @AfterClass + @AfterAll public static void closeConnection() throws SQLException { if (connection != null && !connection.isClosed()) { connection.close(); } } + + public Statement createStatement(String queryResultFormat) throws SQLException { + Statement stmt = connection.createStatement(); + stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); + return stmt; + } } diff --git a/src/test/java/net/snowflake/client/jdbc/BaseWiremockTest.java b/src/test/java/net/snowflake/client/jdbc/BaseWiremockTest.java index 5a2fe8e96..08069b95c 100644 --- a/src/test/java/net/snowflake/client/jdbc/BaseWiremockTest.java +++ b/src/test/java/net/snowflake/client/jdbc/BaseWiremockTest.java @@ -1,12 +1,12 @@ package net.snowflake.client.jdbc; -import static junit.framework.TestCase.assertEquals; import static net.snowflake.client.AbstractDriverIT.getConnectionParameters; +import static net.snowflake.client.AssumptionUtils.assumeNotRunningOnGithubActionsMac; +import static net.snowflake.client.AssumptionUtils.assumeNotRunningOnJava21; +import static net.snowflake.client.AssumptionUtils.assumeNotRunningOnJava8; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; import static org.awaitility.Awaitility.await; -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeNoException; -import static org.junit.Assume.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import java.io.IOException; @@ -16,9 +16,6 @@ import java.time.Duration; import java.util.Map; import java.util.Properties; -import net.snowflake.client.RunningNotOnGithubActionsMac; -import net.snowflake.client.RunningNotOnJava21; -import net.snowflake.client.RunningNotOnJava8; import net.snowflake.client.core.HttpUtil; import net.snowflake.client.log.SFLogger; import net.snowflake.client.log.SFLoggerFactory; @@ -28,11 +25,12 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; -public abstract class BaseWiremockTest { +abstract class BaseWiremockTest { protected static final SFLogger logger = SFLoggerFactory.getLogger(BaseWiremockTest.class); protected static final String WIREMOCK_HOME_DIR = ".wiremock"; @@ -45,27 +43,25 @@ public abstract class BaseWiremockTest { private static String originalTrustStorePath; protected static Process wiremockStandalone; - @BeforeClass + @BeforeAll public static void setUpClass() { - assumeFalse(RunningNotOnJava8.isRunningOnJava8()); - assumeFalse(RunningNotOnJava21.isRunningOnJava21()); - assumeFalse( - RunningNotOnGithubActionsMac - .isRunningOnGithubActionsMac()); // disabled until issue with access to localhost + assumeNotRunningOnJava8(); + assumeNotRunningOnJava21(); + assumeNotRunningOnGithubActionsMac(); // disabled until issue with access to localhost // (https://github.com/snowflakedb/snowflake-jdbc/pull/1807#discussion_r1686229430) is fixed on // github actions mac image. Ticket to enable when fixed: SNOW-1555950 originalTrustStorePath = systemGetProperty(TRUST_STORE_PROPERTY); startWiremockStandAlone(); } - @After + @AfterEach public void tearDown() { restoreTrustStorePathProperty(); resetWiremock(); HttpUtil.httpClient.clear(); } - @AfterClass + @AfterAll public static void tearDownClass() { stopWiremockStandAlone(); } @@ -225,10 +221,10 @@ protected void importMapping(String mappingImport) { HttpPost request = createWiremockPostRequest(mappingImport, "/__admin/mappings/import"); try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = httpClient.execute(request)) { - assumeTrue(response.getStatusLine().getStatusCode() == 200); + Assumptions.assumeTrue(response.getStatusLine().getStatusCode() == 200); } catch (Exception e) { logger.error("Importing mapping failed", e); - assumeNoException(e); + Assumptions.abort("Importing mapping failed"); } } diff --git a/src/test/java/net/snowflake/client/jdbc/BindUploaderIT.java b/src/test/java/net/snowflake/client/jdbc/BindUploaderIT.java index dec8bd6aa..80bd20724 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindUploaderIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindUploaderIT.java @@ -4,9 +4,9 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.sql.Connection; @@ -20,18 +20,18 @@ import java.util.List; import java.util.Map; import java.util.TimeZone; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ParameterBindingDTO; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.bind.BindUploader; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -@Category(TestCategoryOthers.class) +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +@Tag(TestTags.OTHERS) public class BindUploaderIT extends BaseJDBCTest { BindUploader bindUploader; Connection conn; @@ -86,21 +86,21 @@ public class BindUploaderIT extends BaseJDBCTest { + STAGE_DIR + "' ORDER BY $1 ASC"; - @BeforeClass + @BeforeAll public static void classSetUp() throws Exception { Connection connection = getConnection(); connection.createStatement().execute(createTableSQL); connection.close(); } - @AfterClass + @AfterAll public static void classTearDown() throws Exception { Connection connection = getConnection(); connection.createStatement().execute(deleteTableSQL); connection.close(); } - @Before + @BeforeEach public void setUp() throws Exception { conn = getConnection(); session = conn.unwrap(SnowflakeConnectionV1.class).getSfSession(); @@ -109,7 +109,7 @@ public void setUp() throws Exception { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } - @After + @AfterEach public void tearDown() throws SQLException { conn.close(); bindUploader.close(); diff --git a/src/test/java/net/snowflake/client/jdbc/BindUploaderLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindUploaderLatestIT.java index 41c409d8b..badd3fee0 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindUploaderLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindUploaderLatestIT.java @@ -12,8 +12,8 @@ import static net.snowflake.client.jdbc.BindUploaderIT.getBindings; import static net.snowflake.client.jdbc.BindUploaderIT.parseRow; import static net.snowflake.client.jdbc.BindUploaderIT.row1; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import java.sql.Connection; import java.sql.ResultSet; @@ -21,16 +21,16 @@ import java.sql.Statement; import java.util.Map; import java.util.TimeZone; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ParameterBindingDTO; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.bind.BindUploader; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * Bind Uploader tests for the latest JDBC driver. This doesn't work for the oldest supported @@ -38,24 +38,24 @@ * tests still is not applicable. If it is applicable, move tests to BindUploaderIT so that both the * latest and oldest supported driver run the tests. */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class BindUploaderLatestIT extends BaseJDBCTest { BindUploader bindUploader; Connection conn; SFSession session; TimeZone prevTimeZone; // store last time zone and restore after tests - @BeforeClass + @BeforeAll public static void classSetUp() throws Exception { BindUploaderIT.classSetUp(); } - @AfterClass + @AfterAll public static void classTearDown() throws Exception { BindUploaderIT.classTearDown(); } - @Before + @BeforeEach public void setUp() throws Exception { conn = getConnection(); session = conn.unwrap(SnowflakeConnectionV1.class).getSfSession(); @@ -64,7 +64,7 @@ public void setUp() throws Exception { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } - @After + @AfterEach public void tearDown() throws SQLException { conn.close(); bindUploader.close(); diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java index a408e5d5a..55cdf9996 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java @@ -3,11 +3,11 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; @@ -30,40 +30,22 @@ import java.util.TimeZone; import java.util.stream.Collectors; import java.util.stream.Stream; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryResultSet; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.structs.SnowflakeObjectTypeFactories; import net.snowflake.client.jdbc.structuredtypes.sqldata.AllTypesClass; import net.snowflake.client.jdbc.structuredtypes.sqldata.SimpleClass; -import org.junit.After; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -@Category(TestCategoryResultSet.class) +import net.snowflake.client.providers.ResultFormatProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +@Tag(TestTags.RESULT_SET) public class BindingAndInsertingStructuredTypesLatestIT extends BaseJDBCTest { - - @Parameterized.Parameters(name = "format={0}") - public static Object[][] data() { - return new Object[][] { - {ResultSetFormatType.JSON}, - {ResultSetFormatType.ARROW_WITH_JSON_STRUCTURED_TYPES}, - {ResultSetFormatType.NATIVE_ARROW} - }; - } - - private final ResultSetFormatType queryResultFormat; - - public BindingAndInsertingStructuredTypesLatestIT(ResultSetFormatType queryResultFormat) { - this.queryResultFormat = queryResultFormat; - } - - public Connection init() throws SQLException { + public Connection init(ResultSetFormatType queryResultFormat) throws SQLException { Connection conn = BaseJDBCTest.getConnection(BaseJDBCTest.DONT_INJECT_SOCKET_TIMEOUT); try (Statement stmt = conn.createStatement()) { stmt.execute("alter session set ENABLE_STRUCTURED_TYPES_IN_CLIENT_RESPONSE = true"); @@ -84,25 +66,26 @@ public Connection init() throws SQLException { return conn; } - @Before + @BeforeEach public void setup() { SnowflakeObjectTypeFactories.register(SimpleClass.class, SimpleClass::new); SnowflakeObjectTypeFactories.register(AllTypesClass.class, AllTypesClass::new); } - @After + @AfterEach public void clean() { SnowflakeObjectTypeFactories.unregister(SimpleClass.class); SnowflakeObjectTypeFactories.unregister(AllTypesClass.class); } // TODO Structured types feature exists only on QA environments - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteObject() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testWriteObject(ResultSetFormatType queryResultFormat) throws SQLException { SimpleClass sc = new SimpleClass("text1", 2); SimpleClass sc2 = new SimpleClass("text2", 3); - try (Connection connection = init()) { + try (Connection connection = init(queryResultFormat)) { Statement statement = connection.createStatement(); statement.execute( "CREATE OR REPLACE TABLE test_table (ob OBJECT(string varchar, intValue NUMBER))"); @@ -133,11 +116,12 @@ public void testWriteObject() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteNullObject() throws SQLException { - Assume.assumeTrue(queryResultFormat != ResultSetFormatType.NATIVE_ARROW); - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testWriteNullObject(ResultSetFormatType queryResultFormat) throws SQLException { + Assumptions.assumeTrue(queryResultFormat != ResultSetFormatType.NATIVE_ARROW); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmtement2 = (SnowflakePreparedStatementV1) @@ -158,10 +142,12 @@ public void testWriteNullObject() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteObjectBindingNull() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testWriteObjectBindingNull(ResultSetFormatType queryResultFormat) + throws SQLException { + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) @@ -181,11 +167,12 @@ public void testWriteObjectBindingNull() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteObjectAllTypes() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testWriteObjectAllTypes(ResultSetFormatType queryResultFormat) throws SQLException { TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); - try (Connection connection = init(); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) @@ -271,10 +258,11 @@ public static Timestamp toTimestamp(ZonedDateTime dateTime) { return new Timestamp(dateTime.toInstant().getEpochSecond() * 1000L); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteArray() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testWriteArray(ResultSetFormatType queryResultFormat) throws SQLException { + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) @@ -298,10 +286,11 @@ public void testWriteArray() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteArrayNoBinds() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testWriteArrayNoBinds(ResultSetFormatType queryResultFormat) throws SQLException { + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) @@ -322,10 +311,11 @@ public void testWriteArrayNoBinds() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteMapOfSqlData() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testWriteMapOfSqlData(ResultSetFormatType queryResultFormat) throws SQLException { + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) @@ -358,10 +348,11 @@ public void testWriteMapOfSqlData() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteMapOfInteger() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testWriteMapOfInteger(ResultSetFormatType queryResultFormat) throws SQLException { + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) diff --git a/src/test/java/net/snowflake/client/jdbc/BindingDataIT.java b/src/test/java/net/snowflake/client/jdbc/BindingDataIT.java index c2a8bc3ee..86a3b4613 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingDataIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingDataIT.java @@ -6,8 +6,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.sql.Date; import java.sql.PreparedStatement; @@ -18,21 +18,37 @@ import java.sql.Types; import java.util.Calendar; import java.util.TimeZone; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; +import java.util.stream.Stream; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.provider.ValueSource; /** Integration tests for binding variable */ -@RunWith(Theories.class) -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class BindingDataIT extends BaseJDBCWithSharedConnectionIT { - @DataPoints public static short[] shortValues = {0, 1, -1, Short.MIN_VALUE, Short.MAX_VALUE}; + static TimeZone timeZone; - @Theory + @BeforeAll + public static void setTimeZone() { + timeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } + + @AfterAll + public static void resetTimeZone() { + TimeZone.setDefault(timeZone); + } + + @ParameterizedTest + @ValueSource(shorts = {0, 1, -1, Short.MIN_VALUE, Short.MAX_VALUE}) public void testBindShort(short shortValue) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -58,7 +74,8 @@ public void testBindShort(short shortValue) throws SQLException { } } - @Theory + @ParameterizedTest + @ValueSource(shorts = {0, 1, -1, Short.MIN_VALUE, Short.MAX_VALUE}) public void testBindShortViaSetObject(short shortValue) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -84,9 +101,8 @@ public void testBindShortViaSetObject(short shortValue) throws SQLException { } } - @DataPoints public static int[] intValues = {0, 1, -1, Integer.MAX_VALUE, Integer.MIN_VALUE}; - - @Theory + @ParameterizedTest + @ValueSource(ints = {0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}) public void testBindInt(int intValue) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -113,9 +129,8 @@ public void testBindInt(int intValue) throws SQLException { } } - @DataPoints public static byte[] byteValues = {0, 1, -1, Byte.MAX_VALUE, Byte.MIN_VALUE}; - - @Theory + @ParameterizedTest + @ValueSource(bytes = {0, 1, -1, Byte.MAX_VALUE, Byte.MIN_VALUE}) public void testBindByte(byte byteValue) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -184,18 +199,21 @@ public void testBindNull() throws SQLException { } } - @DataPoints - public static Time[] timeValues = { - Time.valueOf("00:00:00"), - Time.valueOf("12:34:56"), - Time.valueOf("12:00:00"), - Time.valueOf("11:59:59"), - Time.valueOf("15:30:00"), - Time.valueOf("13:01:01"), - Time.valueOf("12:00:00"), - }; - - @Theory + static class TimeProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of(Time.valueOf("00:00:00")), + Arguments.of(Time.valueOf("12:34:56")), + Arguments.of(Time.valueOf("12:00:00")), + Arguments.of(Time.valueOf("11:59:59")), + Arguments.of(Time.valueOf("15:30:00")), + Arguments.of(Time.valueOf("13:01:01"))); + } + } + + @ParameterizedTest + @ArgumentsSource(TimeProvider.class) public void testBindTime(Time timeVal) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -225,7 +243,8 @@ public void testBindTime(Time timeVal) throws SQLException { * Bind time with calendar is not supported now. Everything is in UTC, need to revisit in the * future */ - @Theory + @ParameterizedTest + @ArgumentsSource(TimeProvider.class) public void testBindTimeWithCalendar(Time timeVal) throws SQLException { Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); Calendar laCal = Calendar.getInstance(TimeZone.getTimeZone("PST")); @@ -256,7 +275,8 @@ public void testBindTimeWithCalendar(Time timeVal) throws SQLException { } } - @Theory + @ParameterizedTest + @ArgumentsSource(TimeProvider.class) public void testBindTimeViaSetObject(Time timeVal) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -282,7 +302,8 @@ public void testBindTimeViaSetObject(Time timeVal) throws SQLException { } } - @Theory + @ParameterizedTest + @ArgumentsSource(TimeProvider.class) public void testBindTimeViaSetObjectCast(Time timeVal) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -308,18 +329,22 @@ public void testBindTimeViaSetObjectCast(Time timeVal) throws SQLException { } } - @DataPoints - public static Date[] dateValues = { - Date.valueOf("2000-01-01"), - Date.valueOf("3000-01-01"), - Date.valueOf("1970-01-01"), - Date.valueOf("1969-01-01"), - Date.valueOf("1500-01-01"), - Date.valueOf("1400-01-01"), - Date.valueOf("1000-01-01") - }; - - @Theory + static class DateProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of(Date.valueOf("2000-01-01")), + Arguments.of(Date.valueOf("3000-01-01")), + Arguments.of(Date.valueOf("1970-01-01")), + Arguments.of(Date.valueOf("1969-01-01")), + Arguments.of(Date.valueOf("1500-01-01")), + Arguments.of(Date.valueOf("1400-01-01")), + Arguments.of(Date.valueOf("1000-01-01"))); + } + } + + @ParameterizedTest + @ArgumentsSource(DateProvider.class) public void testBindDate(Date dateValue) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -346,7 +371,8 @@ public void testBindDate(Date dateValue) throws SQLException { } } - @Theory + @ParameterizedTest + @ArgumentsSource(DateProvider.class) public void testBindDateWithCalendar(Date dateValue) throws SQLException { Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); @@ -375,7 +401,8 @@ public void testBindDateWithCalendar(Date dateValue) throws SQLException { } } - @Theory + @ParameterizedTest + @ValueSource(ints = {0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}) public void testBindObjectWithScaleZero(int intValue) throws SQLException { try (Statement statement = connection.createStatement()) { try { @@ -429,7 +456,7 @@ public void testBindNullForAllTypes() throws Throwable { while (result.next()) { String testType = result.getString(1); for (int i = 2; i <= 13; ++i) { - assertNull(String.format("Java Type: %s is not null", testType), result.getString(i)); + assertNull(result.getString(i), String.format("Java Type: %s is not null", testType)); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/BindingDataLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingDataLatestIT.java index 71c556686..58298df8a 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingDataLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingDataLatestIT.java @@ -5,8 +5,8 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.PreparedStatement; @@ -17,11 +17,10 @@ import java.util.Calendar; import java.util.TimeZone; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * Binding Data integration tests for the latest JDBC driver. This doesn't work for the oldest @@ -29,7 +28,7 @@ * to examine if the tests still are not applicable. If it is applicable, move tests to * BindingDataIT so that both the latest and oldest supported driver run the tests. */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class BindingDataLatestIT extends AbstractDriverIT { TimeZone origTz = TimeZone.getDefault(); TimeZone tokyoTz = TimeZone.getTimeZone("Asia/Tokyo"); @@ -67,7 +66,7 @@ public void testBindTimestampTZ() throws SQLException { * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testTimestampBindingWithNTZType() throws SQLException { TimeZone.setDefault(tokyoTz); try (Connection connection = getConnection(); @@ -124,7 +123,7 @@ public void testTimestampBindingWithNTZType() throws SQLException { * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testTimestampBindingWithLTZType() throws SQLException { TimeZone.setDefault(tokyoTz); try (Connection connection = getConnection(); @@ -188,7 +187,7 @@ public void testTimestampBindingWithLTZType() throws SQLException { * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testTimestampBindingWithLTZTypeForDayLightSavingTimeZone() throws SQLException { Calendar australia = Calendar.getInstance(australiaTz); TimeZone.setDefault(australiaTz); diff --git a/src/test/java/net/snowflake/client/jdbc/CallableStatementIT.java b/src/test/java/net/snowflake/client/jdbc/CallableStatementIT.java index d6536dc93..1c2900958 100644 --- a/src/test/java/net/snowflake/client/jdbc/CallableStatementIT.java +++ b/src/test/java/net/snowflake/client/jdbc/CallableStatementIT.java @@ -5,8 +5,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.net.URL; @@ -15,74 +15,26 @@ import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Calendar; import java.util.HashMap; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) -@Category(TestCategoryStatement.class) -public class CallableStatementIT extends BaseJDBCTest { - @Parameterized.Parameters - public static Object[][] data() { - // all tests in this class need to run for both query result formats json and arrow - return new Object[][] {{"JSON"}, {"arrow"}}; - } - - private static String queryResultFormat; - - public CallableStatementIT(String format) { - queryResultFormat = format; - } - - public static Connection getConnection() throws SQLException { - Connection conn = BaseJDBCTest.getConnection(); - try (Statement stmt = conn.createStatement()) { - stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); - } - return conn; - } - - private final String createStoredProcedure = - "create or replace procedure square_it(num FLOAT) returns float not " - + "null language javascript as $$ return NUM * NUM; $$"; - private final String createSecondStoredProcedure = - "create or replace procedure add_nums(x DOUBLE, y DOUBLE) " - + "returns double not null language javascript as $$ return X + Y; $$"; - private final String deleteStoredProcedure = "drop procedure if exists square_it(FLOAT)"; - private final String deleteSecondStoredProcedure = "drop procedure if exists add_nums(INT, INT)"; +@Tag(TestTags.STATEMENT) +public class CallableStatementIT extends CallableStatementITBase { - @Before - public void setUp() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - statement.execute(createStoredProcedure); - statement.execute(createSecondStoredProcedure); - } - } - - @After - public void tearDown() throws SQLException { - try (Connection con = getConnection(); - Statement statement = con.createStatement()) { - statement.execute(deleteStoredProcedure); - statement.execute(deleteSecondStoredProcedure); - } - } - - @Test - public void testPrepareCall() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareCall(String queryResultFormat) throws SQLException { // test CallableStatement with no binding parameters - try (Connection connection = getConnection()) { + try (Connection connection = getConnection(queryResultFormat)) { try (CallableStatement callableStatement = connection.prepareCall("call square_it(5)")) { assertThat(callableStatement.getParameterMetaData().getParameterCount(), is(0)); } diff --git a/src/test/java/net/snowflake/client/jdbc/CallableStatementITBase.java b/src/test/java/net/snowflake/client/jdbc/CallableStatementITBase.java new file mode 100644 index 000000000..8635d4246 --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/CallableStatementITBase.java @@ -0,0 +1,48 @@ +package net.snowflake.client.jdbc; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public class CallableStatementITBase extends BaseJDBCTest { + public static Connection getConnection() throws SQLException { + return BaseJDBCTest.getConnection(); + } + + public static Connection getConnection(String queryResultFormat) throws SQLException { + Connection conn = BaseJDBCTest.getConnection(); + try (Statement stmt = conn.createStatement()) { + stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); + } + return conn; + } + + private final String createStoredProcedure = + "create or replace procedure square_it(num FLOAT) returns float not " + + "null language javascript as $$ return NUM * NUM; $$"; + private final String createSecondStoredProcedure = + "create or replace procedure add_nums(x DOUBLE, y DOUBLE) " + + "returns double not null language javascript as $$ return X + Y; $$"; + private final String deleteStoredProcedure = "drop procedure if exists square_it(FLOAT)"; + private final String deleteSecondStoredProcedure = "drop procedure if exists add_nums(INT, INT)"; + + @BeforeEach + public void setUp() throws SQLException { + try (Connection con = getConnection(); + Statement statement = con.createStatement()) { + statement.execute(createStoredProcedure); + statement.execute(createSecondStoredProcedure); + } + } + + @AfterEach + public void tearDown() throws SQLException { + try (Connection con = getConnection(); + Statement statement = con.createStatement()) { + statement.execute(deleteStoredProcedure); + statement.execute(deleteSecondStoredProcedure); + } + } +} diff --git a/src/test/java/net/snowflake/client/jdbc/CallableStatementLatestIT.java b/src/test/java/net/snowflake/client/jdbc/CallableStatementLatestIT.java index a4aaea709..af33e102c 100644 --- a/src/test/java/net/snowflake/client/jdbc/CallableStatementLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/CallableStatementLatestIT.java @@ -2,24 +2,23 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; -@Category(TestCategoryStatement.class) -public class CallableStatementLatestIT extends CallableStatementIT { - - public CallableStatementLatestIT(String format) { - super(format); - } +@Tag(TestTags.STATEMENT) +public class CallableStatementLatestIT extends CallableStatementITBase { /** * Test that function that removes curly brackets from outside of call statements works properly @@ -44,10 +43,11 @@ public void testParseSqlEscapeSyntaxFunction() { * * @throws SQLException */ - @Test - public void testPrepareCallWithCurlyBracketSyntax() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareCallWithCurlyBracketSyntax(String queryResultFormat) throws SQLException { // test CallableStatement with no binding parameters - try (Connection connection = getConnection()) { + try (Connection connection = getConnection(queryResultFormat)) { try (CallableStatement callableStatement = connection.prepareCall("{call square_it(5)}")) { assertThat(callableStatement.getParameterMetaData().getParameterCount(), is(0)); } diff --git a/src/test/java/net/snowflake/client/jdbc/ChunkDownloaderS3RetryUrlLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ChunkDownloaderS3RetryUrlLatestIT.java index cfb8e086d..7824c9a01 100644 --- a/src/test/java/net/snowflake/client/jdbc/ChunkDownloaderS3RetryUrlLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ChunkDownloaderS3RetryUrlLatestIT.java @@ -3,7 +3,7 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertFalse; import java.sql.Connection; import java.sql.ResultSet; @@ -12,7 +12,7 @@ import java.util.List; import java.util.Map; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ExecTimeTelemetryData; import net.snowflake.client.core.HttpUtil; import net.snowflake.client.core.SFBaseSession; @@ -20,18 +20,18 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.CloseableHttpClient; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class ChunkDownloaderS3RetryUrlLatestIT extends AbstractDriverIT { private SFStatement sfStatement; private SFBaseSession sfBaseSession; private ChunkDownloadContext sfContext; - @Before + @BeforeEach public void setup() throws SQLException, InterruptedException { try (Connection connection = getConnection(); Statement statement = connection.createStatement()) { diff --git a/src/test/java/net/snowflake/client/jdbc/ClientMemoryLimitParallelIT.java b/src/test/java/net/snowflake/client/jdbc/ClientMemoryLimitParallelIT.java index 56d954653..b90868b39 100644 --- a/src/test/java/net/snowflake/client/jdbc/ClientMemoryLimitParallelIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ClientMemoryLimitParallelIT.java @@ -1,26 +1,26 @@ package net.snowflake.client.jdbc; import static net.snowflake.client.AbstractDriverIT.getConnection; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author azhan attempts to test the CLIENT_MEMORY_LIMIT working in multi-threading */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class ClientMemoryLimitParallelIT extends BaseJDBCWithSharedConnectionIT { private static Logger LOGGER = LoggerFactory.getLogger(ClientMemoryLimitParallelIT.class.getName()); @@ -62,14 +62,14 @@ public class ClientMemoryLimitParallelIT extends BaseJDBCWithSharedConnectionIT + rowCount + "));"; - @Before + @BeforeEach public void setUp() throws SQLException { try (Statement statement = connection.createStatement()) { statement.execute(createTestTableSQL); } } - @After + @AfterEach public void tearDown() throws SQLException { try (Statement statement = connection.createStatement()) { statement.execute("drop table if exists testtable_cml"); @@ -81,8 +81,8 @@ public void tearDown() throws SQLException { * in multi-threading */ @Test - @Ignore("Long term high memory usage test") - public void testParallelQueries() throws Exception { + @Disabled("Long term high memory usage test") + void testParallelQueries() throws Exception { Runnable testQuery = new Runnable() { public void run() { @@ -122,8 +122,7 @@ public void run() { * make sure there is no hanging */ @Test - public void testQueryNotHanging() throws SQLException { - Properties paramProperties = new Properties(); + void testQueryNotHanging() throws SQLException { try (Statement statement = connection.createStatement()) { queryRows(statement, 100, 160); } diff --git a/src/test/java/net/snowflake/client/jdbc/CompressedStreamFactoryTest.java b/src/test/java/net/snowflake/client/jdbc/CompressedStreamFactoryTest.java index 86eb5764a..0c3f69470 100644 --- a/src/test/java/net/snowflake/client/jdbc/CompressedStreamFactoryTest.java +++ b/src/test/java/net/snowflake/client/jdbc/CompressedStreamFactoryTest.java @@ -1,7 +1,7 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.github.luben.zstd.ZstdInputStream; import com.github.luben.zstd.ZstdOutputStream; @@ -14,7 +14,7 @@ import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.message.BasicHeader; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class CompressedStreamFactoryTest { diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectStringParseTest.java b/src/test/java/net/snowflake/client/jdbc/ConnectStringParseTest.java index 871a6cfcd..c2f7eeb5c 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectStringParseTest.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectStringParseTest.java @@ -2,11 +2,11 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Properties; import net.snowflake.client.core.SFSessionProperty; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class ConnectStringParseTest { @Test diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionAlreadyClosedIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionAlreadyClosedIT.java index fd0b69488..98826eaa8 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionAlreadyClosedIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionAlreadyClosedIT.java @@ -5,11 +5,11 @@ import java.sql.Connection; import java.util.Properties; -import net.snowflake.client.category.TestCategoryConnection; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class ConnectionAlreadyClosedIT extends BaseJDBCTest { @Test diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionFeatureNotSupportedIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionFeatureNotSupportedIT.java index f91eee092..b0b120683 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionFeatureNotSupportedIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionFeatureNotSupportedIT.java @@ -8,11 +8,11 @@ import java.sql.SQLException; import java.sql.Savepoint; import java.util.HashMap; -import net.snowflake.client.category.TestCategoryConnection; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class ConnectionFeatureNotSupportedIT extends BaseJDBCTest { @Test public void testFeatureNotSupportedException() throws Throwable { diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionIT.java index 00656e305..9d99e01a1 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionIT.java @@ -3,16 +3,16 @@ */ package net.snowflake.client.jdbc; +import static net.snowflake.client.AssumptionUtils.assumeRunningOnGithubActions; import static net.snowflake.client.core.SessionUtil.CLIENT_SESSION_KEEP_ALIVE_HEARTBEAT_FREQUENCY; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileInputStream; @@ -42,23 +42,20 @@ import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import net.snowflake.client.ConditionalIgnoreRule.ConditionalIgnore; -import net.snowflake.client.RunningNotOnTestaccount; -import net.snowflake.client.RunningOnGithubAction; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryConnection; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.annotations.RunOnTestaccountNotOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.SFSession; import net.snowflake.common.core.SqlState; import org.apache.commons.codec.binary.Base64; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** Connection integration tests */ -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class ConnectionIT extends BaseJDBCWithSharedConnectionIT { // create a local constant for this code for testing purposes (already defined in GS) public static final int INVALID_CONNECTION_INFO_CODE = 390100; @@ -70,7 +67,7 @@ public class ConnectionIT extends BaseJDBCWithSharedConnectionIT { String errorMessage = null; - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; @Test public void testSimpleConnection() throws SQLException { @@ -86,7 +83,7 @@ public void testSimpleConnection() throws SQLException { } @Test - @Ignore + @Disabled public void test300ConnectionsWithSingleClientInstance() throws SQLException { // concurrent testing int size = 300; @@ -214,7 +211,7 @@ public void testDataCompletenessInLowMemory() throws Exception { } @Test - @ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testConnectionGetAndSetDBAndSchema() throws SQLException { final String SECOND_DATABASE = "SECOND_DATABASE"; final String SECOND_SCHEMA = "SECOND_SCHEMA"; @@ -350,7 +347,7 @@ public void testConnectViaDataSource() throws SQLException { } @Test - @Ignore + @Disabled public void testDataSourceOktaSerialization() throws Exception { // test with username/password authentication // set up DataSource object and ensure connection works @@ -368,7 +365,8 @@ public void testDataSourceOktaSerialization() throws Exception { ResultSet resultSet = statement.executeQuery("select 1")) { resultSet.next(); assertThat("select 1", resultSet.getInt(1), equalTo(1)); - File serializedFile = tmpFolder.newFile("serializedStuff.ser"); + File serializedFile = new File(tmpFolder, "serializedStuff.ser"); + serializedFile.createNewFile(); // serialize datasource object into a file try (FileOutputStream outputFile = new FileOutputStream(serializedFile); ObjectOutputStream out = new ObjectOutputStream(outputFile)) { @@ -391,7 +389,7 @@ public void testDataSourceOktaSerialization() throws Exception { } @Test - @ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testConnectUsingKeyPair() throws Exception { Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); @@ -449,7 +447,7 @@ public void testConnectUsingKeyPair() throws Exception { DriverManager.getConnection(uri, properties); fail(); } catch (SQLException e) { - Assert.assertEquals(390144, e.getErrorCode()); + assertEquals(390144, e.getErrorCode()); } // test multiple key pair try (Connection connection = getConnection(); @@ -506,7 +504,7 @@ public void testBadPrivateKey() throws Exception { } @Test - @ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDifferentKeyLength() throws Exception { Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); @@ -840,7 +838,7 @@ public void testResultSetsClosedByStatement() throws SQLException { } @Test - @ConditionalIgnore(condition = RunningNotOnTestaccount.class) + @RunOnTestaccountNotOnGithubActions public void testOKTAConnection() throws Throwable { Map params = getConnectionParameters(); Properties properties = new Properties(); @@ -857,7 +855,7 @@ public void testOKTAConnection() throws Throwable { } @Test - @ConditionalIgnore(condition = RunningNotOnTestaccount.class) + @RunOnTestaccountNotOnGithubActions public void testOKTAConnectionWithOktauserParam() throws Throwable { Map params = getConnectionParameters(); Properties properties = new Properties(); @@ -888,7 +886,7 @@ public void testValidateDefaultParameters() throws Throwable { fail("should fail"); } catch (SQLException ex) { assertEquals( - "error code", ex.getErrorCode(), SESSION_CREATION_OBJECT_DOES_NOT_EXIST_NOT_AUTHORIZED); + ex.getErrorCode(), SESSION_CREATION_OBJECT_DOES_NOT_EXIST_NOT_AUTHORIZED, "error code"); } // schema is invalid @@ -899,7 +897,7 @@ public void testValidateDefaultParameters() throws Throwable { fail("should fail"); } catch (SQLException ex) { assertEquals( - "error code", ex.getErrorCode(), SESSION_CREATION_OBJECT_DOES_NOT_EXIST_NOT_AUTHORIZED); + ex.getErrorCode(), SESSION_CREATION_OBJECT_DOES_NOT_EXIST_NOT_AUTHORIZED, "error code"); } // warehouse is invalid @@ -910,7 +908,7 @@ public void testValidateDefaultParameters() throws Throwable { fail("should fail"); } catch (SQLException ex) { assertEquals( - "error code", ex.getErrorCode(), SESSION_CREATION_OBJECT_DOES_NOT_EXIST_NOT_AUTHORIZED); + ex.getErrorCode(), SESSION_CREATION_OBJECT_DOES_NOT_EXIST_NOT_AUTHORIZED, "error code"); } // role is invalid @@ -920,7 +918,7 @@ public void testValidateDefaultParameters() throws Throwable { DriverManager.getConnection(params.get("uri"), props); fail("should fail"); } catch (SQLException ex) { - assertEquals("error code", ex.getErrorCode(), ROLE_IN_CONNECT_STRING_DOES_NOT_EXIST); + assertEquals(ex.getErrorCode(), ROLE_IN_CONNECT_STRING_DOES_NOT_EXIST, "error code"); } } @@ -950,7 +948,7 @@ public void testNoValidateDefaultParameters() throws Throwable { DriverManager.getConnection(params.get("uri"), props); fail("should fail"); } catch (SQLException ex) { - assertEquals("error code", ex.getErrorCode(), ROLE_IN_CONNECT_STRING_DOES_NOT_EXIST); + assertEquals(ex.getErrorCode(), ROLE_IN_CONNECT_STRING_DOES_NOT_EXIST, "error code"); } } @@ -961,7 +959,7 @@ public void testNoValidateDefaultParameters() throws Throwable { * * @throws SQLException */ - @Ignore + @Disabled @Test public void testOrgAccountUrl() throws SQLException { Properties props = new Properties(); @@ -987,7 +985,7 @@ public void testOrgAccountUrl() throws SQLException { * @throws SQLException * @throws NoSuchAlgorithmException */ - @Ignore + @Disabled @Test public void testOrgAccountUrlWithKeyPair() throws SQLException, NoSuchAlgorithmException { @@ -1042,7 +1040,7 @@ private Properties setCommonConnectionParameters(boolean validateDefaultParamete @Test public void testFailOverOrgAccount() throws SQLException { // only when set_git_info.sh picks up a SOURCE_PARAMETER_FILE - assumeTrue(RunningOnGithubAction.isRunningOnGithubAction()); + assumeRunningOnGithubActions(); Map kvParams = getConnectionParameters(null, "ORG"); Properties connProps = kvMap2Properties(kvParams, false); diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index 30ff6728f..68cd101bf 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -14,13 +14,13 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.core.AnyOf.anyOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -51,11 +51,10 @@ import java.util.Properties; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLHandshakeException; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningNotOnAWS; -import net.snowflake.client.RunningOnGithubAction; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryConnection; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.annotations.RunOnAWS; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.HttpClientSettingsKey; import net.snowflake.client.core.HttpUtil; import net.snowflake.client.core.ObjectMapperFactory; @@ -73,13 +72,12 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** * Connection integration tests for the latest JDBC driver. This doesn't work for the oldest @@ -87,14 +85,14 @@ * if the tests still is not applicable. If it is applicable, move tests to ConnectionIT so that * both the latest and oldest supported driver run the tests. */ -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class ConnectionLatestIT extends BaseJDBCTest { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; private static final SFLogger logger = SFLoggerFactory.getLogger(ConnectionLatestIT.class); private boolean defaultState; - @Before + @BeforeEach public void setUp() { TelemetryService service = TelemetryService.getInstance(); service.updateContextForIT(getConnectionParameters()); @@ -103,7 +101,7 @@ public void setUp() { TelemetryService.enable(); } - @After + @AfterEach public void tearDown() throws InterruptedException { TelemetryService service = TelemetryService.getInstance(); // wait 5 seconds while the service is flushing @@ -193,12 +191,13 @@ public void testHeartbeatFrequencyTooSmall() throws Exception { * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void putGetStatementsHaveQueryID() throws Throwable { try (Connection con = getConnection(); Statement statement = con.createStatement()) { String sourceFilePath = getFullPathFileInResource(TEST_DATA_FILE); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); statement.execute("CREATE OR REPLACE STAGE testPutGet_stage"); SnowflakeStatement snowflakeStatement = statement.unwrap(SnowflakeStatement.class); @@ -210,7 +209,7 @@ public void putGetStatementsHaveQueryID() throws Throwable { String statementPutQueryId = snowflakeStatement.getQueryID(); TestUtil.assertValidQueryId(statementPutQueryId); assertNotEquals( - "create query id is override by put query id", createStageQueryId, statementPutQueryId); + createStageQueryId, statementPutQueryId, "create query id is override by put query id"); resultSetPutQueryId = resultSet.unwrap(SnowflakeResultSet.class).getQueryID(); TestUtil.assertValidQueryId(resultSetPutQueryId); assertEquals(resultSetPutQueryId, statementPutQueryId); @@ -222,7 +221,7 @@ public void putGetStatementsHaveQueryID() throws Throwable { String resultSetGetQueryId = resultSet.unwrap(SnowflakeResultSet.class).getQueryID(); TestUtil.assertValidQueryId(resultSetGetQueryId); assertNotEquals( - "put and get query id should be different", resultSetGetQueryId, resultSetPutQueryId); + resultSetGetQueryId, resultSetPutQueryId, "put and get query id should be different"); assertEquals(resultSetGetQueryId, statementGetQueryId); } } @@ -230,12 +229,13 @@ public void putGetStatementsHaveQueryID() throws Throwable { /** Added in > 3.14.4 */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void putGetStatementsHaveQueryIDEvenWhenFail() throws Throwable { try (Connection con = getConnection(); Statement statement = con.createStatement()) { String sourceFilePath = getFullPathFileInResource(TEST_DATA_FILE); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); SnowflakeStatement snowflakeStatement = statement.unwrap(SnowflakeStatement.class); try { @@ -255,7 +255,7 @@ public void putGetStatementsHaveQueryIDEvenWhenFail() throws Throwable { assertEquals(snowflakeStatement.getQueryID(), e.getQueryId()); } String getQueryId = snowflakeStatement.getQueryID(); - assertNotEquals("put and get query id should be different", putQueryId, getQueryId); + assertNotEquals(putQueryId, getQueryId, "put and get query id should be different"); String stageName = "stage_" + SnowflakeUtil.randomAlphaNumeric(10); statement.execute("CREATE OR REPLACE STAGE " + stageName); TestUtil.assertValidQueryId(snowflakeStatement.getQueryID()); @@ -707,7 +707,7 @@ public void testHttpsLoginTimeoutWithSSL() throws InterruptedException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testKeyPairFileDataSourceSerialization() throws Exception { // test with key/pair authentication where key is in file // set up DataSource object and ensure connection works @@ -727,7 +727,8 @@ public void testKeyPairFileDataSourceSerialization() throws Exception { connectAndExecuteSelect1(ds); - File serializedFile = tmpFolder.newFile("serializedStuff.ser"); + File serializedFile = new File(tmpFolder, "serializedStuff.ser"); + serializedFile.createNewFile(); // serialize datasource object into a file try (FileOutputStream outputFile = new FileOutputStream(serializedFile); ObjectOutputStream out = new ObjectOutputStream(outputFile)) { @@ -752,7 +753,7 @@ private static String readPrivateKeyFileToBase64Content(String fileName) throws /** Works in > 3.18.0 */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testKeyPairBase64DataSourceSerialization() throws Exception { // test with key/pair authentication where key is passed as a Base64 string value // set up DataSource object and ensure connection works @@ -772,7 +773,8 @@ public void testKeyPairBase64DataSourceSerialization() throws Exception { connectAndExecuteSelect1(ds); - File serializedFile = tmpFolder.newFile("serializedStuff.ser"); + File serializedFile = new File(tmpFolder, "serializedStuff.ser"); + serializedFile.createNewFile(); // serialize datasource object into a file try (FileOutputStream outputFile = new FileOutputStream(serializedFile); ObjectOutputStream out = new ObjectOutputStream(outputFile)) { @@ -795,7 +797,7 @@ public void testKeyPairBase64DataSourceSerialization() throws Exception { * executions */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPrivateKeyInConnectionString() throws SQLException, IOException { Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); @@ -898,7 +900,7 @@ private static void unsetPublicKey(String testUser) throws SQLException { // This will only work with JDBC driver versions higher than 3.15.1 @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPrivateKeyInConnectionStringWithBouncyCastle() throws SQLException, IOException { System.setProperty(SecurityUtil.ENABLE_BOUNCYCASTLE_PROVIDER_JVM, "true"); testPrivateKeyInConnectionString(); @@ -911,7 +913,7 @@ public void testPrivateKeyInConnectionStringWithBouncyCastle() throws SQLExcepti * executions */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPrivateKeyBase64InConnectionString() throws SQLException, IOException { Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); @@ -999,7 +1001,7 @@ private static void connectExpectingInvalidOrUnsupportedPrivateKey( /** Works in > 3.18.0 */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPrivateKeyBase64InConnectionStringWithBouncyCastle() throws SQLException, IOException { System.setProperty(SecurityUtil.ENABLE_BOUNCYCASTLE_PROVIDER_JVM, "true"); @@ -1007,7 +1009,7 @@ public void testPrivateKeyBase64InConnectionStringWithBouncyCastle() } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testBasicDataSourceSerialization() throws Exception { // test with username/password authentication // set up DataSource object and ensure connection works @@ -1022,7 +1024,8 @@ public void testBasicDataSourceSerialization() throws Exception { connectAndExecuteSelect1(ds); - File serializedFile = tmpFolder.newFile("serializedStuff.ser"); + File serializedFile = new File(tmpFolder, "serializedStuff.ser"); + serializedFile.createNewFile(); // serialize datasource object into a file try (FileOutputStream outputFile = new FileOutputStream(serializedFile); ObjectOutputStream out = new ObjectOutputStream(outputFile)) { @@ -1233,7 +1236,7 @@ public void testGetChildQueryIdsNegativeTestQueryFailed() throws Exception { * likely not having the test account we used here. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testAuthenticatorEndpointWithDashInAccountName() throws Exception { Map params = getConnectionParameters(); String serverUrl = @@ -1292,7 +1295,7 @@ public void testReadOnly() throws Throwable { * the error code is ErrorCode.S3_OPERATION_ERROR so only runs on AWS. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningNotOnAWS.class) + @RunOnAWS public void testDownloadStreamWithFileNotFoundException() throws SQLException { try (Connection connection = getConnection(); Statement statement = connection.createStatement()) { @@ -1354,19 +1357,19 @@ private Boolean isPbes2KeySupported() throws SQLException, IOException, Security String passphrase = System.getenv(passphraseEnv); assertNotNull( + passphrase, privateKeyFileNameEnv + " environment variable can't be empty. " - + "Please provide the filename for your private key located in the resource folder", - passphrase); + + "Please provide the filename for your private key located in the resource folder"); assertNotNull( + passphrase, publicKeyFileNameEnv + " environment variable can't be empty. " - + "Please provide the filename for your public key located in the resource folder", - passphrase); + + "Please provide the filename for your public key located in the resource folder"); assertNotNull( - passphraseEnv + " environment variable is required to decrypt private key.", passphrase); + passphrase, passphraseEnv + " environment variable is required to decrypt private key."); Map parameters = getConnectionParameters(); String testUser = parameters.get("user"); Properties properties = new Properties(); @@ -1424,8 +1427,8 @@ private Boolean isPbes2KeySupported() throws SQLException, IOException, Security * @throws IOException */ @Test - @Ignore - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @Disabled + @DontRunOnGithubActions public void testPbes2Support() throws SQLException, IOException { System.clearProperty(SecurityUtil.ENABLE_BOUNCYCASTLE_PROVIDER_JVM); boolean pbes2Supported = isPbes2KeySupported(); @@ -1438,7 +1441,7 @@ public void testPbes2Support() throws SQLException, IOException { String failureMessage = "The failure means that the JDK version can decrypt a private key generated by OpenSSL v3 and " + "BouncyCastle shouldn't be needed anymore"; - assertFalse(failureMessage, pbes2Supported); + assertFalse(pbes2Supported, failureMessage); // The expectation is that this is going to pass once we add Bouncy Castle in the list of // providers @@ -1448,12 +1451,12 @@ public void testPbes2Support() throws SQLException, IOException { "Bouncy Castle Provider should have been loaded with the -D" + SecurityUtil.ENABLE_BOUNCYCASTLE_PROVIDER_JVM + "JVM argument and this should have decrypted the private key generated by OpenSSL v3"; - assertTrue(failureMessage, pbes2Supported); + assertTrue(pbes2Supported, failureMessage); } // Test for regenerating okta one-time token for versions > 3.15.1 @Test - @Ignore + @Disabled public void testDataSourceOktaGenerates429StatusCode() throws Exception { // test with username/password authentication // set up DataSource object and ensure connection works @@ -1551,26 +1554,26 @@ public void shouldGetDifferentTimestampLtzConsistentBetweenFormats() throws Exce arrowResultSet.getTimestamp(column).getTimezoneOffset(), arrowResultSet.getTimestamp(column).getClass()); assertEquals( + jsonResultSet.getString(column), + arrowResultSet.getString(column), "Expecting that string representation are the same for row " + rowIdx + " and column " - + column, - jsonResultSet.getString(column), - arrowResultSet.getString(column)); + + column); assertEquals( + jsonResultSet.getTimestamp(column).toString(), + arrowResultSet.getTimestamp(column).toString(), "Expecting that string representation (via toString) are the same for row " + rowIdx + " and column " - + column, - jsonResultSet.getTimestamp(column).toString(), - arrowResultSet.getTimestamp(column).toString()); + + column); assertEquals( + jsonResultSet.getTimestamp(column), + arrowResultSet.getTimestamp(column), "Expecting that timestamps are the same for row " + rowIdx + " and column " - + column, - jsonResultSet.getTimestamp(column), - arrowResultSet.getTimestamp(column)); + + column); } rowIdx++; } diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionManual.java b/src/test/java/net/snowflake/client/jdbc/ConnectionManual.java index 91d5f7bc8..4b7d569d5 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionManual.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionManual.java @@ -1,6 +1,6 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionPoolingIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionPoolingIT.java index 770acda0a..a539dc7f9 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionPoolingIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionPoolingIT.java @@ -3,7 +3,7 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.mchange.v2.c3p0.ComboPooledDataSource; import com.zaxxer.hikari.HikariConfig; @@ -15,17 +15,17 @@ import java.sql.Statement; import java.util.Map; import java.util.Properties; -import net.snowflake.client.category.TestCategoryConnection; +import net.snowflake.client.category.TestTags; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.PoolingDataSource; import org.apache.commons.pool.impl.GenericObjectPool; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Connection pool interface test */ -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class ConnectionPoolingIT { private BasicDataSource bds = null; private ComboPooledDataSource cpds = null; @@ -48,7 +48,7 @@ public ConnectionPoolingIT() { ssl = params.get("ssl"); } - @Before + @BeforeEach public void setUp() throws SQLException { try (Connection connection = BaseJDBCTest.getConnection(); Statement statement = connection.createStatement()) { @@ -57,7 +57,7 @@ public void setUp() throws SQLException { } } - @After + @AfterEach public void tearDown() throws SQLException { try (Connection connection = BaseJDBCTest.getConnection(); Statement statement = connection.createStatement(); ) { diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java index 49c6c6d10..96b896247 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionWithOCSPModeIT.java @@ -9,8 +9,8 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; import java.net.SocketTimeoutException; import java.security.cert.CertificateExpiredException; @@ -19,17 +19,16 @@ import java.util.Properties; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryConnection; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.SFOCSPException; import net.snowflake.client.core.SFTrustManager; import org.hamcrest.Matcher; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * Tests for connection with OCSP mode mainly negative cases by injecting errors. @@ -38,7 +37,7 @@ * *

hang_webserver.py 12345 */ -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class ConnectionWithOCSPModeIT extends BaseJDBCTest { private final String testUser = "fakeuser"; private final String testPassword = "testpassword"; @@ -46,12 +45,12 @@ public class ConnectionWithOCSPModeIT extends BaseJDBCTest { private static int nameCounter = 0; - @Before + @BeforeEach public void setUp() { SFTrustManager.deleteCache(); } - @After + @AfterEach public void tearDown() { SFTrustManager.cleanTestSystemParameters(); } @@ -340,7 +339,7 @@ public void testOCSPResponderTimeoutFailOpen() { /** Test OCSP Responder hang and timeout. SocketTimeoutException exception should be raised. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testOCSPResponderTimeoutFailClosed() { System.setProperty(SFTrustManager.SF_OCSP_TEST_OCSP_RESPONDER_TIMEOUT, "1000"); System.setProperty(SFTrustManager.SF_OCSP_TEST_RESPONDER_URL, "http://localhost:12345/hang"); @@ -380,7 +379,7 @@ public void testOCSPResponder403FailOpen() { * is invalid. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testOCSPResponder403FailClosed() { System.setProperty(SFTrustManager.SF_OCSP_TEST_RESPONDER_URL, "http://localhost:12345/403"); System.setProperty( @@ -397,7 +396,7 @@ public void testOCSPResponder403FailClosed() { /** Test Certificate Expired. Will fail in both FAIL_OPEN and FAIL_CLOSED. */ @Test - @Ignore("Issuer of root CA expired") + @Disabled("Issuer of root CA expired") // https://support.sectigo.com/articles/Knowledge/Sectigo-AddTrust-External-CA-Root-Expiring-May-30-2020 public void testExpiredCert() { try { diff --git a/src/test/java/net/snowflake/client/jdbc/CustomProxyLatestIT.java b/src/test/java/net/snowflake/client/jdbc/CustomProxyLatestIT.java index c6fb29bf4..2673d543c 100644 --- a/src/test/java/net/snowflake/client/jdbc/CustomProxyLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/CustomProxyLatestIT.java @@ -1,13 +1,13 @@ package net.snowflake.client.jdbc; -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.fail; import static net.snowflake.client.AbstractDriverIT.getFullPathFileInResource; import static net.snowflake.client.jdbc.SnowflakeDriverIT.findFile; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.net.Authenticator; @@ -18,17 +18,16 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.HttpClientSettingsKey; import net.snowflake.client.core.HttpProtocol; import net.snowflake.client.core.HttpUtil; import net.snowflake.client.core.SFSession; import net.snowflake.common.core.SqlState; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; // To run these tests, you must: // 1.) Start up a proxy connection. The simplest ways are via Squid or BurpSuite. Confluence doc on @@ -37,9 +36,9 @@ // 2.) Enter your own username and password for the account you're connecting to // 3.) Adjust parameters like role, database, schema, etc to match with account accordingly -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class CustomProxyLatestIT { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; /** * Before running this test, change the user and password to appropriate values. Set up 2 @@ -51,7 +50,7 @@ public class CustomProxyLatestIT { * @throws SQLException */ @Test - @Ignore + @Disabled public void test2ProxiesWithSameJVM() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -107,7 +106,7 @@ public void test2ProxiesWithSameJVM() throws SQLException { * @throws SQLException */ @Test - @Ignore + @Disabled public void testTLSIssue() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -149,7 +148,7 @@ public void testTLSIssue() throws SQLException { * http instead of https proxy parameters for non-TLS proxy */ @Test - @Ignore + @Disabled public void testJVMParamsWithNonProxyHostsHonored() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -172,7 +171,7 @@ public void testJVMParamsWithNonProxyHostsHonored() throws SQLException { /** Test TLS issue against S3 client to ensure proxy works with PUT/GET statements */ @Test - @Ignore + @Disabled public void testTLSIssueWithConnectionStringAgainstS3() throws ClassNotFoundException, SQLException { @@ -193,7 +192,7 @@ public void testTLSIssueWithConnectionStringAgainstS3() * @throws SQLException */ @Test - @Ignore + @Disabled public void testNonProxyHostAltering() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -243,7 +242,7 @@ public void testNonProxyHostAltering() throws SQLException { * @throws SQLException */ @Test - @Ignore + @Disabled public void testSizeOfHttpClientNoProxies() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -279,7 +278,7 @@ public void testSizeOfHttpClientNoProxies() throws SQLException { } @Test - @Ignore + @Disabled public void testCorrectProxySettingFromConnectionString() throws ClassNotFoundException, SQLException { String connectionUrl = @@ -299,7 +298,7 @@ public void testCorrectProxySettingFromConnectionString() } @Test - @Ignore + @Disabled public void testWrongProxyPortSettingFromConnectionString() throws ClassNotFoundException, SQLException { @@ -313,7 +312,7 @@ public void testWrongProxyPortSettingFromConnectionString() } @Test - @Ignore + @Disabled public void testWrongProxyPasswordSettingFromConnectionString() throws ClassNotFoundException, SQLException { @@ -334,7 +333,7 @@ public void testWrongProxyPasswordSettingFromConnectionString() } @Test - @Ignore + @Disabled public void testInvalidProxyPortFromConnectionString() throws ClassNotFoundException, SQLException { @@ -355,7 +354,7 @@ public void testInvalidProxyPortFromConnectionString() } @Test - @Ignore + @Disabled public void testNonProxyHostsFromConnectionString() throws ClassNotFoundException, SQLException { String connectionUrl = @@ -368,7 +367,7 @@ public void testNonProxyHostsFromConnectionString() throws ClassNotFoundExceptio } @Test - @Ignore + @Disabled public void testWrongNonProxyHostsFromConnectionString() throws ClassNotFoundException, SQLException { @@ -383,7 +382,7 @@ public void testWrongNonProxyHostsFromConnectionString() } @Test - @Ignore + @Disabled public void testUnsetJvmPropertiesForInvalidSettings() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -435,11 +434,9 @@ public PasswordAuthentication getPasswordAuthentication() { stmt.execute("use warehouse TINY_WAREHOUSE"); stmt.execute("CREATE OR REPLACE STAGE testPutGet_stage"); assertTrue( - "Failed to put a file", stmt.execute( - "PUT file://" - + getFullPathFileInResource("orders_100.csv") - + " @testPutGet_stage")); + "PUT file://" + getFullPathFileInResource("orders_100.csv") + " @testPutGet_stage"), + "Failed to put a file"); String sql = "select $1 from values(1),(3),(5),(7)"; try (ResultSet res = stmt.executeQuery(sql)) { while (res.next()) { @@ -454,7 +451,7 @@ public PasswordAuthentication getPasswordAuthentication() { } @Test - @Ignore + @Disabled public void testProxyConnectionWithAzure() throws ClassNotFoundException, SQLException { String connectionUrl = "jdbc:snowflake://aztestaccount.east-us-2.azure.snowflakecomputing.com/?tracing=ALL"; @@ -463,7 +460,7 @@ public void testProxyConnectionWithAzure() throws ClassNotFoundException, SQLExc } @Test - @Ignore + @Disabled public void testProxyConnectionWithAzureWithConnectionString() throws ClassNotFoundException, SQLException { String connectionUrl = @@ -476,7 +473,7 @@ public void testProxyConnectionWithAzureWithConnectionString() } @Test - @Ignore + @Disabled public void testProxyConnectionWithoutProxyPortOrHost() throws ClassNotFoundException, SQLException { // proxyPort is empty @@ -553,7 +550,7 @@ public void testProxyConnectionWithoutProxyPortOrHost() * @throws SQLException */ @Test - @Ignore + @Disabled public void testProxyConnectionWithJVMParameters() throws SQLException, ClassNotFoundException { String connectionUrl = "jdbc:snowflake://aztestaccount.east-us-2.azure.snowflakecomputing.com/?tracing=ALL"; @@ -571,7 +568,7 @@ public void testProxyConnectionWithJVMParameters() throws SQLException, ClassNot } @Test - @Ignore + @Disabled public void testProxyConnectionWithAzureWithWrongConnectionString() throws ClassNotFoundException { String connectionUrl = @@ -598,7 +595,7 @@ public void testProxyConnectionWithAzureWithWrongConnectionString() * is specified. Set up a http proxy and change the settings below. */ @Test - @Ignore + @Disabled public void testSetJVMProxyHttp() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -624,7 +621,7 @@ public void testSetJVMProxyHttp() throws SQLException { * below. */ @Test - @Ignore + @Disabled public void testSetJVMProxyHttps() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -649,7 +646,7 @@ public void testSetJVMProxyHttps() throws SQLException { * https proxy and change the settings below. */ @Test - @Ignore + @Disabled public void testSetJVMProxyDefaultHttps() throws SQLException { Properties props = new Properties(); props.put("user", "USER"); @@ -725,19 +722,20 @@ public PasswordAuthentication getPasswordAuthentication() { String TEST_DATA_FILE = "orders_100.csv"; String sourceFilePath = getFullPathFileInResource(TEST_DATA_FILE); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; assertTrue( - "Failed to put a file", - stmt.execute("PUT file://" + sourceFilePath + " @testPutGet_stage")); + stmt.execute("PUT file://" + sourceFilePath + " @testPutGet_stage"), + "Failed to put a file"); findFile(stmt, "ls @testPutGet_stage/"); // download the file we just uploaded to stage assertTrue( - "Failed to get a file", stmt.execute( - "GET @testPutGet_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @testPutGet_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get a file"); // Make sure that the downloaded file exists, it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + TEST_DATA_FILE + ".gz"); diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataIT.java index ce3130761..8f1f5b964 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataIT.java @@ -5,15 +5,15 @@ import static java.sql.DatabaseMetaData.procedureReturnsResult; import static java.sql.ResultSetMetaData.columnNullableUnknown; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasItem; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.google.common.base.Strings; import java.sql.Connection; @@ -28,15 +28,14 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Database Metadata IT */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class DatabaseMetaDataIT extends BaseJDBCWithSharedConnectionIT { private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)(?:\\.\\d+)+\\s*.*"); @@ -205,7 +204,7 @@ public void testGetTableTypes() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetTables() throws Throwable { Set tables = null; try (Statement statement = connection.createStatement()) { @@ -564,7 +563,7 @@ public void testProcedure() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetTablePrivileges() throws Exception { try (Statement statement = connection.createStatement()) { String database = connection.getCatalog(); diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataInternalIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataInternalIT.java index ec590b066..00838fd1b 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataInternalIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataInternalIT.java @@ -5,9 +5,9 @@ import static net.snowflake.client.jdbc.DatabaseMetaDataIT.EXPECTED_MAX_BINARY_LENGTH; import static net.snowflake.client.jdbc.DatabaseMetaDataIT.verifyResultSetMetaDataColumns; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -15,24 +15,23 @@ import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Database Metadata IT */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class DatabaseMetaDataInternalIT extends BaseJDBCTest { private Connection connection; private Statement statement; private DatabaseMetaData databaseMetaData; private ResultSet resultSet; - @Before + @BeforeEach public void setUp() throws SQLException { try (Connection con = getConnection()) { initMetaData(con); @@ -68,7 +67,7 @@ static void initMetaData(Connection con) throws SQLException { } } - @After + @AfterEach public void tearDown() throws SQLException { try (Connection con = getConnection()) { endMetaData(con); @@ -83,7 +82,8 @@ static void endMetaData(Connection con) throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @Disabled // TODO: SNOW-1805299 + @DontRunOnGithubActions public void testGetColumn() throws SQLException { String getAllColumnsCount = "select count(*) from db.information_schema.columns"; connection = getConnection(); @@ -166,7 +166,7 @@ public void testGetColumn() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetFunctions() throws SQLException { connection = getConnection(); statement = connection.createStatement(); @@ -241,7 +241,8 @@ public void testGetFunctions() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @Disabled // TODO: SNOW-1805299 + @DontRunOnGithubActions public void testGetSchema() throws SQLException { String getSchemaCount = "select count(*) from db.information_schema.schemata"; connection = getConnection(); @@ -290,9 +291,9 @@ public void testGetSchema() throws SQLException { * getTables() function Author: Andong Zhan Created on 09/28/2018 */ @Test - @Ignore // SNOW-85084 detected this is a flaky test, so ignore it here. + @Disabled // SNOW-85084 detected this is a flaky test, so ignore it here. // We have other regression tests to cover it - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetTablesReusingCachedResults() throws SQLException { Connection snowflakeConnection = getSnowflakeAdminConnection(); Statement snowflake = snowflakeConnection.createStatement(); @@ -449,7 +450,8 @@ private long getAccountId(Statement stmt, String accountName) throws SQLExceptio } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @Disabled // TODO: SNOW-1805299 + @DontRunOnGithubActions public void testGetTables() throws SQLException { String getAllTable = "select count(*) from db.information_schema.tables"; String getAllBaseTable = @@ -579,7 +581,7 @@ public void testGetTables() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetMetaDataUseConnectionCtx() throws SQLException { try (Connection connection = getConnection(); Statement statement = connection.createStatement()) { diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataInternalLatestIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataInternalLatestIT.java index 15701ca17..622f94a0a 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataInternalLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataInternalLatestIT.java @@ -2,8 +2,8 @@ import static net.snowflake.client.jdbc.DatabaseMetaDataInternalIT.endMetaData; import static net.snowflake.client.jdbc.DatabaseMetaDataInternalIT.initMetaData; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -17,13 +17,12 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * Database Metadata tests for the latest JDBC driver. This doesn't work for the oldest supported @@ -31,17 +30,17 @@ * tests still is not applicable. If it is applicable, move tests to DatabaseMetaDataIT so that both * the latest and oldest supported driver run the tests. */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class DatabaseMetaDataInternalLatestIT extends BaseJDBCTest { - @Before + @BeforeEach public void setUp() throws Exception { try (Connection con = getConnection()) { initMetaData(con); } } - @After + @AfterEach public void tearDown() throws Exception { try (Connection con = getConnection()) { endMetaData(con); @@ -49,7 +48,7 @@ public void tearDown() throws Exception { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetMetaDataUseConnectionCtx() throws SQLException { try (Connection connection = getConnection(); Statement statement = connection.createStatement()) { @@ -79,7 +78,7 @@ public void testGetMetaDataUseConnectionCtx() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetFunctionColumns() throws SQLException { try (Connection connection = getConnection(); Statement statement = connection.createStatement()) { @@ -253,7 +252,7 @@ public void testGetFunctionColumns() throws SQLException { /** Tests that calling getTables() concurrently doesn't cause data race condition. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetTablesRaceCondition() throws SQLException, ExecutionException, InterruptedException { try (Connection connection = getConnection()) { diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataLatestIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataLatestIT.java index 082907502..c038be49e 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataLatestIT.java @@ -9,13 +9,13 @@ import static net.snowflake.client.jdbc.SnowflakeDatabaseMetaData.NumericFunctionsSupported; import static net.snowflake.client.jdbc.SnowflakeDatabaseMetaData.StringFunctionsSupported; import static net.snowflake.client.jdbc.SnowflakeDatabaseMetaData.SystemFunctionsSupported; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.lang.reflect.Field; import java.sql.Connection; @@ -30,16 +30,15 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SFSessionProperty; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * DatabaseMetaData test for the latest JDBC driver. This doesn't work for the oldest supported @@ -47,7 +46,7 @@ * tests still is not applicable. If it is applicable, move tests to DatabaseMetaDataIT so that both * the latest and oldest supported driver run the tests. */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class DatabaseMetaDataLatestIT extends BaseJDBCWithSharedConnectionIT { private static final String TEST_PROC = "create or replace procedure testproc(param1 float, param2 string)\n" @@ -99,7 +98,7 @@ public void createDoubleQuotedSchemaAndCatalog(Statement statement) throws SQLEx statement.execute("create or replace schema \"dbwith\"\"quotes\".\"schemawith\"\"quotes\""); } - @Before + @BeforeEach public void setUp() throws SQLException { try (Statement stmt = connection.createStatement()) { stmt.execute("USE DATABASE " + startingDatabase); @@ -272,7 +271,7 @@ public void testDoubleQuotedDatabaseAndSchema() throws Exception { * This tests the ability to have quotes inside a database or schema within getSchemas() function. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDoubleQuotedDatabaseInGetSchemas() throws SQLException { try (Statement statement = connection.createStatement()) { // Create a database with double quotes inside the database name @@ -300,7 +299,7 @@ public void testDoubleQuotedDatabaseInGetSchemas() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDoubleQuotedDatabaseInGetTables() throws SQLException { try (Statement statement = connection.createStatement()) { // Create a database with double quotes inside the database name @@ -316,7 +315,7 @@ public void testDoubleQuotedDatabaseInGetTables() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDoubleQuotedDatabaseInGetColumns() throws SQLException { try (Statement statement = connection.createStatement()) { // Create a database and schema with double quotes inside the database name @@ -332,7 +331,7 @@ public void testDoubleQuotedDatabaseInGetColumns() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDoubleQuotedDatabaseforGetPrimaryKeysAndForeignKeys() throws SQLException { try (Statement statement = connection.createStatement()) { // Create a database and schema with double quotes inside the database name @@ -361,7 +360,7 @@ public void testDoubleQuotedDatabaseforGetPrimaryKeysAndForeignKeys() throws SQL * getPrimaryKeys and getImportedKeys functions by setting enablePatternSearch = false. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDoubleQuotedDatabaseforGetPrimaryKeysAndForeignKeysWithPatternSearchDisabled() throws SQLException { Properties properties = new Properties(); @@ -390,7 +389,7 @@ public void testDoubleQuotedDatabaseforGetPrimaryKeysAndForeignKeysWithPatternSe } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDoubleQuotedDatabaseInGetProcedures() throws SQLException { try (Statement statement = connection.createStatement()) { // Create a database and schema with double quotes inside the database name @@ -407,7 +406,7 @@ public void testDoubleQuotedDatabaseInGetProcedures() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDoubleQuotedDatabaseInGetTablePrivileges() throws SQLException { try (Statement statement = connection.createStatement()) { // Create a database and schema with double quotes inside the database name @@ -589,7 +588,7 @@ public void testGetColumnsNullable() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testSessionDatabaseParameter() throws Throwable { String altdb = "ALTERNATEDB"; String altschema1 = "ALTERNATESCHEMA1"; @@ -756,7 +755,7 @@ public void testSessionDatabaseParameter() throws Throwable { * returns 1 row per return value and 1 row per input parameter. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetFunctionColumns() throws Exception { try (Statement statement = connection.createStatement()) { String database = startingDatabase; @@ -1667,7 +1666,7 @@ public void testGetStreams() throws SQLException { * This tests that an empty resultset will be returned for getProcedures when using a reader account. */ @Test - @Ignore + @Disabled public void testGetProceduresWithReaderAccount() throws SQLException { DatabaseMetaData metadata = connection.getMetaData(); try (ResultSet rs = metadata.getProcedures(null, null, null)) { @@ -1676,7 +1675,7 @@ public void testGetProceduresWithReaderAccount() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetProcedureColumns() throws Exception { try (Statement statement = connection.createStatement()) { String database = startingDatabase; diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultSetLatestIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultSetLatestIT.java index f69260a69..2a62be5a2 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultSetLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultSetLatestIT.java @@ -3,9 +3,10 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -15,14 +16,14 @@ import java.sql.Types; import java.util.Arrays; import java.util.List; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class DatabaseMetaDataResultSetLatestIT extends BaseJDBCTest { - @Test(expected = SnowflakeLoggedFeatureNotSupportedException.class) + @Test public void testGetObjectNotSupported() throws SQLException { try (Connection con = getConnection(); Statement st = con.createStatement()) { @@ -34,7 +35,9 @@ public void testGetObjectNotSupported() throws SQLException { new SnowflakeDatabaseMetaDataResultSet( columnNames, columnTypeNames, columnTypes, rows, st)) { resultSet.next(); - assertEquals(1.2F, resultSet.getObject(1)); + assertThrows( + SnowflakeLoggedFeatureNotSupportedException.class, + () -> assertEquals(1.2F, resultSet.getObject(1))); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultsetIT.java b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultsetIT.java index ccc984e3c..605ca3698 100644 --- a/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultsetIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DatabaseMetaDataResultsetIT.java @@ -3,9 +3,9 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.sql.Date; @@ -17,11 +17,11 @@ import java.sql.Types; import java.util.Arrays; import java.util.List; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class DatabaseMetaDataResultsetIT extends BaseJDBCWithSharedConnectionIT { private static final int columnCount = 9; private static final int INT_DATA = 1; diff --git a/src/test/java/net/snowflake/client/jdbc/DellBoomiCloudIT.java b/src/test/java/net/snowflake/client/jdbc/DellBoomiCloudIT.java index 794af78df..bd2680be1 100644 --- a/src/test/java/net/snowflake/client/jdbc/DellBoomiCloudIT.java +++ b/src/test/java/net/snowflake/client/jdbc/DellBoomiCloudIT.java @@ -7,15 +7,16 @@ import java.sql.SQLException; import java.sql.Statement; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** A simple run on fetch result under boomi cloud environment's policy file */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class DellBoomiCloudIT extends AbstractDriverIT { - @Before + @BeforeEach public void setup() { File file = new File(DellBoomiCloudIT.class.getResource("boomi.policy").getFile()); @@ -25,6 +26,7 @@ public void setup() { } @Test + @Disabled // TODO: SNOW-1805239 public void testSelectLargeResultSet() throws SQLException { try (Connection connection = getConnection(); Statement statement = connection.createStatement(); diff --git a/src/test/java/net/snowflake/client/jdbc/FileConnectionConfigurationLatestIT.java b/src/test/java/net/snowflake/client/jdbc/FileConnectionConfigurationLatestIT.java index 734446c92..5474488b3 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileConnectionConfigurationLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/FileConnectionConfigurationLatestIT.java @@ -4,22 +4,23 @@ package net.snowflake.client.jdbc; import static net.snowflake.client.config.SFConnectionConfigParser.SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import org.junit.After; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; /** This test could be run only on environment where file connection.toml is configured */ -@Ignore +@Disabled public class FileConnectionConfigurationLatestIT { - @After + @AfterEach public void cleanUp() { SnowflakeUtil.systemUnsetEnv(SNOWFLAKE_DEFAULT_CONNECTION_NAME_KEY); } @@ -27,7 +28,7 @@ public void cleanUp() { @Test public void testThrowExceptionIfConfigurationDoesNotExist() { SnowflakeUtil.systemSetEnv("SNOWFLAKE_DEFAULT_CONNECTION_NAME", "non-existent"); - Assert.assertThrows(SnowflakeSQLException.class, () -> SnowflakeDriver.INSTANCE.connect()); + assertThrows(SnowflakeSQLException.class, () -> SnowflakeDriver.INSTANCE.connect()); } @Test @@ -46,7 +47,7 @@ private static void verifyConnetionToSnowflake(String connectionName) throws SQL DriverManager.getConnection(SnowflakeDriver.AUTO_CONNECTION_STRING_PREFIX, null); Statement statement = con.createStatement(); ResultSet resultSet = statement.executeQuery("show parameters")) { - Assert.assertTrue(resultSet.next()); + assertTrue(resultSet.next()); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderExpandFileNamesTest.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderExpandFileNamesTest.java index c874b3e33..8545ca998 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderExpandFileNamesTest.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderExpandFileNamesTest.java @@ -4,9 +4,9 @@ package net.snowflake.client.jdbc; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; @@ -23,23 +23,20 @@ import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import net.snowflake.client.core.OCSPMode; -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** Tests for SnowflakeFileTransferAgent.expandFileNames */ public class FileUploaderExpandFileNamesTest { - @Rule public TemporaryFolder folder = new TemporaryFolder(); - @Rule public TemporaryFolder secondFolder = new TemporaryFolder(); + @TempDir private File folder; private String localFSFileSep = systemGetProperty("file.separator"); @Test public void testProcessFileNames() throws Exception { - folder.newFile("TestFileA"); - folder.newFile("TestFileB"); + new File(folder, "TestFileA").createNewFile(); + new File(folder, "TestFileB").createNewFile(); - String folderName = folder.getRoot().getCanonicalPath(); + String folderName = folder.getCanonicalPath(); String originalUserDir = System.getProperty("user.dir"); String originalUserHome = System.getProperty("user.home"); System.setProperty("user.dir", folderName); @@ -82,8 +79,8 @@ public void testProcessFileNamesException() { try { SnowflakeFileTransferAgent.expandFileNames(locations, null); } catch (SnowflakeSQLException err) { - Assert.assertEquals(200007, err.getErrorCode()); - Assert.assertEquals("22000", err.getSQLState()); + assertEquals(200007, err.getErrorCode()); + assertEquals("22000", err.getSQLState()); } SnowflakeFileTransferAgent.setInjectedFileTransferException(null); } @@ -163,8 +160,8 @@ public int read() throws IOException { */ @Test public void testFileListingDoesNotFailOnMissingFilesOfAnotherPattern() throws Exception { - folder.newFolder("TestFiles"); - String folderName = folder.getRoot().getCanonicalPath(); + new File(folder, "TestFiles").mkdirs(); + String folderName = folder.getCanonicalPath(); int filePatterns = 10; int filesPerPattern = 100; @@ -224,8 +221,8 @@ public void testFileListingDoesNotFailOnMissingFilesOfAnotherPattern() throws Ex @Test public void testFileListingDoesNotFailOnNotExistingDirectory() throws Exception { - folder.newFolder("TestFiles"); - String folderName = folder.getRoot().getCanonicalPath(); + new File(folder, "TestFiles").mkdirs(); + String folderName = folder.getCanonicalPath(); String[] locations = { folderName + localFSFileSep + "foo*", }; diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderLatestIT.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderLatestIT.java index 995832362..a116a794b 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderLatestIT.java @@ -5,10 +5,10 @@ import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.amazonaws.services.s3.model.ObjectMetadata; import com.fasterxml.jackson.databind.ObjectMapper; @@ -32,9 +32,8 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.OCSPMode; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.SFStatement; @@ -47,12 +46,11 @@ import net.snowflake.client.jdbc.cloud.storage.StorageProviderException; import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; import org.apache.commons.io.FileUtils; -import org.junit.Assert; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Tests for SnowflakeFileTransferAgent that require an active connection */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class FileUploaderLatestIT extends FileUploaderPrep { private static final String OBJ_META_STAGE = "testObjMeta"; private ObjectMapper mapper = new ObjectMapper(); @@ -65,7 +63,7 @@ public class FileUploaderLatestIT extends FileUploaderPrep { * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetS3StageDataWithS3Session() throws SQLException { try (Connection con = getConnection("s3testaccount")) { SFSession sfSession = con.unwrap(SnowflakeConnectionV1.class).getSfSession(); @@ -74,16 +72,16 @@ public void testGetS3StageDataWithS3Session() throws SQLException { // Get sample stage info with session StageInfo stageInfo = SnowflakeFileTransferAgent.getStageInfo(exampleS3JsonNode, sfSession); - Assert.assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); + assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); // Assert that true value from session is reflected in StageInfo - Assert.assertEquals(true, stageInfo.getUseS3RegionalUrl()); + assertEquals(true, stageInfo.getUseS3RegionalUrl()); // Set UseRegionalS3EndpointsForPresignedURL to false in session sfSession.setUseRegionalS3EndpointsForPresignedURL(false); stageInfo = SnowflakeFileTransferAgent.getStageInfo(exampleS3JsonNode, sfSession); - Assert.assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); + assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); // Assert that false value from session is reflected in StageInfo - Assert.assertEquals(false, stageInfo.getUseS3RegionalUrl()); + assertEquals(false, stageInfo.getUseS3RegionalUrl()); } } @@ -94,7 +92,7 @@ public void testGetS3StageDataWithS3Session() throws SQLException { * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetS3StageDataWithAzureSession() throws SQLException { try (Connection con = getConnection("azureaccount")) { SFSession sfSession = con.unwrap(SnowflakeConnectionV1.class).getSfSession(); @@ -106,18 +104,18 @@ public void testGetS3StageDataWithAzureSession() throws SQLException { // Get sample stage info with session StageInfo stageInfo = SnowflakeFileTransferAgent.getStageInfo(exampleAzureJsonNode, sfSession); - Assert.assertEquals(StageInfo.StageType.AZURE, stageInfo.getStageType()); - Assert.assertEquals("EXAMPLE_LOCATION/", stageInfo.getLocation()); + assertEquals(StageInfo.StageType.AZURE, stageInfo.getStageType()); + assertEquals("EXAMPLE_LOCATION/", stageInfo.getLocation()); // Assert that UseRegionalS3EndpointsForPresignedURL is false in StageInfo even if it was set // to // true. // The value should always be false for non-S3 accounts - Assert.assertEquals(false, stageInfo.getUseS3RegionalUrl()); + assertEquals(false, stageInfo.getUseS3RegionalUrl()); } } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetObjectMetadataWithGCS() throws Exception { Properties paramProperties = new Properties(); paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", true); @@ -143,7 +141,7 @@ public void testGetObjectMetadataWithGCS() throws Exception { String remoteStageLocation = location.substring(0, idx); String path = location.substring(idx + 1) + TEST_DATA_FILE + ".gz"; StorageObjectMetadata metadata = client.getObjectMetadata(remoteStageLocation, path); - Assert.assertEquals("gzip", metadata.getContentEncoding()); + assertEquals("gzip", metadata.getContentEncoding()); } finally { statement.execute("DROP STAGE if exists " + OBJ_META_STAGE); } @@ -151,7 +149,7 @@ public void testGetObjectMetadataWithGCS() throws Exception { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetObjectMetadataFileNotFoundWithGCS() throws Exception { Properties paramProperties = new Properties(); paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", true); @@ -180,8 +178,8 @@ public void testGetObjectMetadataFileNotFoundWithGCS() throws Exception { fail("should raise exception"); } catch (Exception ex) { assertTrue( - "Wrong type of exception. Message: " + ex.getMessage(), - ex instanceof StorageProviderException); + ex instanceof StorageProviderException, + "Wrong type of exception. Message: " + ex.getMessage()); assertTrue(ex.getMessage().matches(".*Blob.*not found in bucket.*")); } finally { statement.execute("DROP STAGE if exists " + OBJ_META_STAGE); @@ -190,7 +188,7 @@ public void testGetObjectMetadataFileNotFoundWithGCS() throws Exception { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetObjectMetadataStorageExceptionWithGCS() throws Exception { Properties paramProperties = new Properties(); paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", true); @@ -218,8 +216,8 @@ public void testGetObjectMetadataStorageExceptionWithGCS() throws Exception { fail("should raise exception"); } catch (Exception ex) { assertTrue( - "Wrong type of exception. Message: " + ex.getMessage(), - ex instanceof StorageProviderException); + ex instanceof StorageProviderException, + "Wrong type of exception. Message: " + ex.getMessage()); assertTrue(ex.getMessage().matches(".*Permission.*denied.*")); } finally { statement.execute("DROP STAGE if exists " + OBJ_META_STAGE); @@ -253,8 +251,8 @@ public void testNullCommand() throws SQLException { SnowflakeFileTransferAgent sfAgent = new SnowflakeFileTransferAgent(null, sfSession, new SFStatement(sfSession)); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue( + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertTrue( err.getMessage() .contains("JDBC driver internal error: Missing sql for statement execution")); } finally { @@ -294,8 +292,8 @@ public void testCompressStreamWithGzipException() throws Exception { .setCommand(PUT_COMMAND) .build()); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue( + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertTrue( err.getMessage() .contains("JDBC driver internal error: error encountered for compression")); } finally { @@ -338,8 +336,8 @@ public void testCompressStreamWithGzipNoDigestException() throws Exception { .setCommand(PUT_COMMAND) .build()); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue( + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertTrue( err.getMessage() .contains("JDBC driver internal error: error encountered for compression")); } finally { @@ -382,7 +380,7 @@ public void testUploadWithoutConnectionException() throws Exception { .setCommand(PUT_COMMAND) .build()); } catch (Exception err) { - Assert.assertTrue( + assertTrue( err.getMessage() .contains( "Exception encountered during file upload: failed to push to remote store")); @@ -405,7 +403,7 @@ public void testInitFileMetadataFileNotFound() throws Exception { sfAgent.execute(); } catch (SnowflakeSQLException err) { - Assert.assertEquals(200008, err.getErrorCode()); + assertEquals(200008, err.getErrorCode()); } finally { statement.execute("DROP STAGE if exists testStage"); } @@ -426,7 +424,7 @@ public void testInitFileMetadataFileIsDirectory() throws Exception { new SnowflakeFileTransferAgent(command, sfSession, new SFStatement(sfSession)); sfAgent.execute(); } catch (SnowflakeSQLException err) { - Assert.assertEquals(200009, err.getErrorCode()); + assertEquals(200009, err.getErrorCode()); } finally { statement.execute("DROP STAGE if exists testStage"); } @@ -449,8 +447,8 @@ public void testCompareAndSkipFilesException() throws Exception { sfAgent.execute(); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue(err.getMessage().contains("Error reading:")); + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertTrue(err.getMessage().contains("Error reading:")); } finally { statement.execute("DROP STAGE if exists testStage"); } @@ -472,8 +470,8 @@ public void testParseCommandException() throws SQLException { new SnowflakeFileTransferAgent(PUT_COMMAND, sfSession, new SFStatement(sfSession)); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertTrue(err.getMessage().contains("Failed to parse the locations")); + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertTrue(err.getMessage().contains("Failed to parse the locations")); } finally { statement.execute("DROP STAGE if exists testStage"); } @@ -534,8 +532,8 @@ public void testListObjectsStorageException() throws Exception { sfAgent.execute(); } catch (SnowflakeSQLException err) { - Assert.assertEquals(200016, err.getErrorCode()); - Assert.assertTrue(err.getMessage().contains("Encountered exception during listObjects")); + assertEquals(200016, err.getErrorCode()); + assertTrue(err.getMessage().contains("Encountered exception during listObjects")); } finally { statement.execute("DROP STAGE if exists testStage"); } @@ -563,7 +561,7 @@ public void testUploadStreamInterruptedException() throws IOException, SQLExcept "~", DEST_PREFIX, outputStream.asByteSource().openStream(), "hello.txt", false); } catch (SnowflakeSQLLoggedException err) { - Assert.assertEquals(200003, err.getErrorCode()); + assertEquals(200003, err.getErrorCode()); } finally { statement.execute("rm @~/" + DEST_PREFIX); } @@ -666,7 +664,7 @@ public void testUploadFileStreamWithNoOverwrite() throws Exception { assertEquals(expectedValue, actualValue); } } catch (Exception e) { - Assert.fail("testUploadFileStreamWithNoOverwrite failed " + e.getMessage()); + fail("testUploadFileStreamWithNoOverwrite failed " + e.getMessage()); } finally { statement.execute("DROP STAGE if exists testStage"); } @@ -696,7 +694,7 @@ public void testUploadFileStreamWithOverwrite() throws Exception { assertFalse(expectedValue.equals(actualValue)); } } catch (Exception e) { - Assert.fail("testUploadFileStreamWithNoOverwrite failed " + e.getMessage()); + fail("testUploadFileStreamWithNoOverwrite failed " + e.getMessage()); } finally { statement.execute("DROP STAGE if exists testStage"); } @@ -704,7 +702,7 @@ public void testUploadFileStreamWithOverwrite() throws Exception { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetS3StorageObjectMetadata() throws Throwable { try (Connection connection = getConnection("s3testaccount"); Statement statement = connection.createStatement()) { diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderMimeTypeToCompressionTypeTest.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderMimeTypeToCompressionTypeTest.java index d9ad6d2c1..418c70a05 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderMimeTypeToCompressionTypeTest.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderMimeTypeToCompressionTypeTest.java @@ -3,55 +3,49 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Arrays; -import java.util.Collection; import java.util.Optional; +import java.util.stream.Stream; import net.snowflake.common.core.FileCompressionType; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; /** * Tests for SnowflakeFileTransferAgent.mimeTypeToCompressionType See * https://github.com/apache/tika/blob/master/tika-core/src/main/resources/org/apache/tika/mime/tika-mimetypes.xml * for test cases */ -@RunWith(Parameterized.class) public class FileUploaderMimeTypeToCompressionTypeTest { - private final String mimeType; - private final FileCompressionType mimeSubType; - public FileUploaderMimeTypeToCompressionTypeTest( - String mimeType, FileCompressionType mimeSubType) { - this.mimeType = mimeType; - this.mimeSubType = mimeSubType; - } - - @Parameterized.Parameters(name = "mimeType={0}, mimeSubType={1}") - public static Collection primeNumbers() { - return Arrays.asList( - new Object[][] { - {"text/", null}, - {"text/csv", null}, - {"snowflake/orc", FileCompressionType.ORC}, - {"snowflake/orc;p=1", FileCompressionType.ORC}, - {"snowflake/parquet", FileCompressionType.PARQUET}, - {"application/zlib", FileCompressionType.DEFLATE}, - {"application/x-bzip2", FileCompressionType.BZIP2}, - {"application/zstd", FileCompressionType.ZSTD}, - {"application/x-brotli", FileCompressionType.BROTLI}, - {"application/x-lzip", FileCompressionType.LZIP}, - {"application/x-lzma", FileCompressionType.LZMA}, - {"application/x-xz", FileCompressionType.XZ}, - {"application/x-compress", FileCompressionType.COMPRESS}, - {"application/x-gzip", FileCompressionType.GZIP} - }); + static class MimeTypesProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of("text/", null), + Arguments.of("text/csv", null), + Arguments.of("snowflake/orc", FileCompressionType.ORC), + Arguments.of("snowflake/orc;p=1", FileCompressionType.ORC), + Arguments.of("snowflake/parquet", FileCompressionType.PARQUET), + Arguments.of("application/zlib", FileCompressionType.DEFLATE), + Arguments.of("application/x-bzip2", FileCompressionType.BZIP2), + Arguments.of("application/zstd", FileCompressionType.ZSTD), + Arguments.of("application/x-brotli", FileCompressionType.BROTLI), + Arguments.of("application/x-lzip", FileCompressionType.LZIP), + Arguments.of("application/x-lzma", FileCompressionType.LZMA), + Arguments.of("application/x-xz", FileCompressionType.XZ), + Arguments.of("application/x-compress", FileCompressionType.COMPRESS), + Arguments.of("application/x-gzip", FileCompressionType.GZIP)); + } } - @Test - public void testMimeTypeToCompressionType() throws Throwable { + @ParameterizedTest + @ArgumentsSource(MimeTypesProvider.class) + public void testMimeTypeToCompressionType(String mimeType, FileCompressionType mimeSubType) + throws Throwable { Optional foundCompType = SnowflakeFileTransferAgent.mimeTypeToCompressionType(mimeType); if (foundCompType.isPresent()) { diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java index d801a00ac..276eb0234 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderPrep.java @@ -10,13 +10,10 @@ import java.io.InputStream; import java.util.Arrays; import java.util.List; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeAll; /** File uploader test prep reused by IT/connection tests and sessionless tests */ abstract class FileUploaderPrep extends BaseJDBCTest { - @Rule public TemporaryFolder folder = new TemporaryFolder(); private static final ObjectMapper mapper = new ObjectMapper(); @@ -35,7 +32,7 @@ private static JsonNode readJsonFromFile(String name) throws IOException { } } - @BeforeClass + @BeforeAll public static void setup() throws Exception { exampleS3JsonNode = readJsonFromFile("exampleS3"); exampleS3StageEndpointJsonNode = readJsonFromFile("exampleS3WithStageEndpoint"); diff --git a/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java b/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java index f5fb7f719..1c74869f0 100644 --- a/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java +++ b/src/test/java/net/snowflake/client/jdbc/FileUploaderSessionlessTest.java @@ -3,10 +3,11 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -19,8 +20,7 @@ import java.util.Optional; import net.snowflake.client.jdbc.cloud.storage.StageInfo; import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Tests for SnowflakeFileTransferAgent.expandFileNames. */ public class FileUploaderSessionlessTest extends FileUploaderPrep { @@ -37,8 +37,8 @@ public void testGetEncryptionMaterialMissing() throws Exception { SnowflakeFileTransferAgent.getEncryptionMaterial( SFBaseFileTransferAgent.CommandType.UPLOAD, modifiedNode); - Assert.assertEquals(1, encryptionMaterials.size()); - Assert.assertNull(encryptionMaterials.get(0)); + assertEquals(1, encryptionMaterials.size()); + assertNull(encryptionMaterials.get(0)); } @Test @@ -54,12 +54,12 @@ public void testGetEncryptionMaterial() throws Exception { SnowflakeFileTransferAgent.getEncryptionMaterial( SFBaseFileTransferAgent.CommandType.UPLOAD, exampleNode); - Assert.assertEquals(1, encryptionMaterials.size()); - Assert.assertEquals( + assertEquals(1, encryptionMaterials.size()); + assertEquals( expected.get(0).getQueryStageMasterKey(), encryptionMaterials.get(0).getQueryStageMasterKey()); - Assert.assertEquals(expected.get(0).getQueryId(), encryptionMaterials.get(0).getQueryId()); - Assert.assertEquals(expected.get(0).getSmkId(), encryptionMaterials.get(0).getSmkId()); + assertEquals(expected.get(0).getQueryId(), encryptionMaterials.get(0).getQueryId()); + assertEquals(expected.get(0).getSmkId(), encryptionMaterials.get(0).getSmkId()); } } @@ -73,14 +73,14 @@ public void testGetS3StageData() throws Exception { expectedCreds.put("AWS_SECRET_KEY", "EXAMPLE_AWS_SECRET_KEY"); expectedCreds.put("AWS_TOKEN", "EXAMPLE_AWS_TOKEN"); - Assert.assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); - Assert.assertEquals("stage/location/foo/", stageInfo.getLocation()); - Assert.assertEquals(expectedCreds, stageInfo.getCredentials()); - Assert.assertEquals("us-west-2", stageInfo.getRegion()); - Assert.assertEquals("null", stageInfo.getEndPoint()); - Assert.assertEquals(null, stageInfo.getStorageAccount()); - Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); - Assert.assertEquals(true, stageInfo.getUseS3RegionalUrl()); + assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); + assertEquals("stage/location/foo/", stageInfo.getLocation()); + assertEquals(expectedCreds, stageInfo.getCredentials()); + assertEquals("us-west-2", stageInfo.getRegion()); + assertEquals("null", stageInfo.getEndPoint()); + assertEquals(null, stageInfo.getStorageAccount()); + assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(true, stageInfo.getUseS3RegionalUrl()); } @Test @@ -94,13 +94,13 @@ public void testGetS3StageDataWithStageEndpoint() throws Exception { expectedCreds.put("AWS_SECRET_KEY", "EXAMPLE_AWS_SECRET_KEY"); expectedCreds.put("AWS_TOKEN", "EXAMPLE_AWS_TOKEN"); - Assert.assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); - Assert.assertEquals("stage/location/foo/", stageInfo.getLocation()); - Assert.assertEquals(expectedCreds, stageInfo.getCredentials()); - Assert.assertEquals("us-west-2", stageInfo.getRegion()); - Assert.assertEquals("s3-fips.us-east-1.amazonaws.com", stageInfo.getEndPoint()); - Assert.assertEquals(null, stageInfo.getStorageAccount()); - Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); + assertEquals("stage/location/foo/", stageInfo.getLocation()); + assertEquals(expectedCreds, stageInfo.getCredentials()); + assertEquals("us-west-2", stageInfo.getRegion()); + assertEquals("s3-fips.us-east-1.amazonaws.com", stageInfo.getEndPoint()); + assertEquals(null, stageInfo.getStorageAccount()); + assertEquals(true, stageInfo.getIsClientSideEncrypted()); } @Test @@ -109,13 +109,13 @@ public void testGetAzureStageData() throws Exception { Map expectedCreds = new HashMap<>(); expectedCreds.put("AZURE_SAS_TOKEN", "EXAMPLE_AZURE_SAS_TOKEN"); - Assert.assertEquals(StageInfo.StageType.AZURE, stageInfo.getStageType()); - Assert.assertEquals("EXAMPLE_LOCATION/", stageInfo.getLocation()); - Assert.assertEquals(expectedCreds, stageInfo.getCredentials()); - Assert.assertEquals("westus", stageInfo.getRegion()); - Assert.assertEquals("blob.core.windows.net", stageInfo.getEndPoint()); - Assert.assertEquals("EXAMPLE_STORAGE_ACCOUNT", stageInfo.getStorageAccount()); - Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(StageInfo.StageType.AZURE, stageInfo.getStageType()); + assertEquals("EXAMPLE_LOCATION/", stageInfo.getLocation()); + assertEquals(expectedCreds, stageInfo.getCredentials()); + assertEquals("westus", stageInfo.getRegion()); + assertEquals("blob.core.windows.net", stageInfo.getEndPoint()); + assertEquals("EXAMPLE_STORAGE_ACCOUNT", stageInfo.getStorageAccount()); + assertEquals(true, stageInfo.getIsClientSideEncrypted()); } @Test @@ -123,20 +123,20 @@ public void testGetGCSStageData() throws Exception { StageInfo stageInfo = SnowflakeFileTransferAgent.getStageInfo(exampleGCSJsonNode, null); Map expectedCreds = new HashMap<>(); - Assert.assertEquals(StageInfo.StageType.GCS, stageInfo.getStageType()); - Assert.assertEquals("foo/tables/9224/", stageInfo.getLocation()); - Assert.assertEquals(expectedCreds, stageInfo.getCredentials()); - Assert.assertEquals("US-WEST1", stageInfo.getRegion()); - Assert.assertEquals(null, stageInfo.getEndPoint()); - Assert.assertEquals(null, stageInfo.getStorageAccount()); - Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(StageInfo.StageType.GCS, stageInfo.getStageType()); + assertEquals("foo/tables/9224/", stageInfo.getLocation()); + assertEquals(expectedCreds, stageInfo.getCredentials()); + assertEquals("US-WEST1", stageInfo.getRegion()); + assertEquals(null, stageInfo.getEndPoint()); + assertEquals(null, stageInfo.getStorageAccount()); + assertEquals(true, stageInfo.getIsClientSideEncrypted()); } @Test public void testGetFileTransferMetadatasS3() throws Exception { List metadataList = SnowflakeFileTransferAgent.getFileTransferMetadatas(exampleS3JsonNode); - Assert.assertEquals(1, metadataList.size()); + assertEquals(1, metadataList.size()); SnowflakeFileTransferMetadataV1 metadata = (SnowflakeFileTransferMetadataV1) metadataList.get(0); @@ -151,25 +151,25 @@ public void testGetFileTransferMetadatasS3() throws Exception { expectedCreds.put("AWS_SECRET_KEY", "EXAMPLE_AWS_SECRET_KEY"); expectedCreds.put("AWS_TOKEN", "EXAMPLE_AWS_TOKEN"); - Assert.assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); - Assert.assertEquals("stage/location/foo/", stageInfo.getLocation()); - Assert.assertEquals(expectedCreds, stageInfo.getCredentials()); - Assert.assertEquals("us-west-2", stageInfo.getRegion()); - Assert.assertEquals("null", stageInfo.getEndPoint()); - Assert.assertEquals(null, stageInfo.getStorageAccount()); - Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); + assertEquals("stage/location/foo/", stageInfo.getLocation()); + assertEquals(expectedCreds, stageInfo.getCredentials()); + assertEquals("us-west-2", stageInfo.getRegion()); + assertEquals("null", stageInfo.getEndPoint()); + assertEquals(null, stageInfo.getStorageAccount()); + assertEquals(true, stageInfo.getIsClientSideEncrypted()); // EncryptionMaterial check - Assert.assertEquals("EXAMPLE_QUERY_ID", metadata.getEncryptionMaterial().getQueryId()); - Assert.assertEquals( + assertEquals("EXAMPLE_QUERY_ID", metadata.getEncryptionMaterial().getQueryId()); + assertEquals( "EXAMPLE_QUERY_STAGE_MASTER_KEY", metadata.getEncryptionMaterial().getQueryStageMasterKey()); - Assert.assertEquals(123L, (long) metadata.getEncryptionMaterial().getSmkId()); + assertEquals(123L, (long) metadata.getEncryptionMaterial().getSmkId()); // Misc check - Assert.assertEquals(SFBaseFileTransferAgent.CommandType.UPLOAD, metadata.getCommandType()); - Assert.assertNull(metadata.getPresignedUrl()); - Assert.assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); + assertEquals(SFBaseFileTransferAgent.CommandType.UPLOAD, metadata.getCommandType()); + assertNull(metadata.getPresignedUrl()); + assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); } @Test @@ -180,7 +180,7 @@ public void testGetFileTransferMetadatasS3MissingEncryption() throws Exception { List metadataList = SnowflakeFileTransferAgent.getFileTransferMetadatas(modifiedNode); - Assert.assertEquals(1, metadataList.size()); + assertEquals(1, metadataList.size()); SnowflakeFileTransferMetadataV1 metadata = (SnowflakeFileTransferMetadataV1) metadataList.get(0); @@ -195,30 +195,30 @@ public void testGetFileTransferMetadatasS3MissingEncryption() throws Exception { expectedCreds.put("AWS_SECRET_KEY", "EXAMPLE_AWS_SECRET_KEY"); expectedCreds.put("AWS_TOKEN", "EXAMPLE_AWS_TOKEN"); - Assert.assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); - Assert.assertEquals("stage/location/foo/", stageInfo.getLocation()); - Assert.assertEquals(expectedCreds, stageInfo.getCredentials()); - Assert.assertEquals("us-west-2", stageInfo.getRegion()); - Assert.assertEquals("null", stageInfo.getEndPoint()); - Assert.assertEquals(null, stageInfo.getStorageAccount()); - Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(StageInfo.StageType.S3, stageInfo.getStageType()); + assertEquals("stage/location/foo/", stageInfo.getLocation()); + assertEquals(expectedCreds, stageInfo.getCredentials()); + assertEquals("us-west-2", stageInfo.getRegion()); + assertEquals("null", stageInfo.getEndPoint()); + assertEquals(null, stageInfo.getStorageAccount()); + assertEquals(true, stageInfo.getIsClientSideEncrypted()); // EncryptionMaterial check - Assert.assertNull(metadata.getEncryptionMaterial().getQueryId()); - Assert.assertNull(metadata.getEncryptionMaterial().getQueryStageMasterKey()); - Assert.assertNull(metadata.getEncryptionMaterial().getSmkId()); + assertNull(metadata.getEncryptionMaterial().getQueryId()); + assertNull(metadata.getEncryptionMaterial().getQueryStageMasterKey()); + assertNull(metadata.getEncryptionMaterial().getSmkId()); // Misc check - Assert.assertEquals(SFBaseFileTransferAgent.CommandType.UPLOAD, metadata.getCommandType()); - Assert.assertNull(metadata.getPresignedUrl()); - Assert.assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); + assertEquals(SFBaseFileTransferAgent.CommandType.UPLOAD, metadata.getCommandType()); + assertNull(metadata.getPresignedUrl()); + assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); } @Test public void testGetFileTransferMetadatasAzure() throws Exception { List metadataList = SnowflakeFileTransferAgent.getFileTransferMetadatas(exampleAzureJsonNode); - Assert.assertEquals(1, metadataList.size()); + assertEquals(1, metadataList.size()); SnowflakeFileTransferMetadataV1 metadata = (SnowflakeFileTransferMetadataV1) metadataList.get(0); @@ -229,32 +229,32 @@ public void testGetFileTransferMetadatasAzure() throws Exception { Map expectedCreds = new HashMap<>(); expectedCreds.put("AZURE_SAS_TOKEN", "EXAMPLE_AZURE_SAS_TOKEN"); - Assert.assertEquals(StageInfo.StageType.AZURE, stageInfo.getStageType()); - Assert.assertEquals("EXAMPLE_LOCATION/", stageInfo.getLocation()); - Assert.assertEquals(expectedCreds, stageInfo.getCredentials()); - Assert.assertEquals("westus", stageInfo.getRegion()); - Assert.assertEquals("blob.core.windows.net", stageInfo.getEndPoint()); - Assert.assertEquals("EXAMPLE_STORAGE_ACCOUNT", stageInfo.getStorageAccount()); - Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(StageInfo.StageType.AZURE, stageInfo.getStageType()); + assertEquals("EXAMPLE_LOCATION/", stageInfo.getLocation()); + assertEquals(expectedCreds, stageInfo.getCredentials()); + assertEquals("westus", stageInfo.getRegion()); + assertEquals("blob.core.windows.net", stageInfo.getEndPoint()); + assertEquals("EXAMPLE_STORAGE_ACCOUNT", stageInfo.getStorageAccount()); + assertEquals(true, stageInfo.getIsClientSideEncrypted()); // EncryptionMaterial check - Assert.assertEquals("EXAMPLE_QUERY_ID", metadata.getEncryptionMaterial().getQueryId()); - Assert.assertEquals( + assertEquals("EXAMPLE_QUERY_ID", metadata.getEncryptionMaterial().getQueryId()); + assertEquals( "EXAMPLE_QUERY_STAGE_MASTER_KEY", metadata.getEncryptionMaterial().getQueryStageMasterKey()); - Assert.assertEquals(123L, (long) metadata.getEncryptionMaterial().getSmkId()); + assertEquals(123L, (long) metadata.getEncryptionMaterial().getSmkId()); // Misc check - Assert.assertEquals(SFBaseFileTransferAgent.CommandType.UPLOAD, metadata.getCommandType()); - Assert.assertNull(metadata.getPresignedUrl()); - Assert.assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); + assertEquals(SFBaseFileTransferAgent.CommandType.UPLOAD, metadata.getCommandType()); + assertNull(metadata.getPresignedUrl()); + assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); } @Test public void testGetFileTransferMetadatasGCS() throws Exception { List metadataList = SnowflakeFileTransferAgent.getFileTransferMetadatas(exampleGCSJsonNode); - Assert.assertEquals(1, metadataList.size()); + assertEquals(1, metadataList.size()); SnowflakeFileTransferMetadataV1 metadata = (SnowflakeFileTransferMetadataV1) metadataList.get(0); @@ -264,33 +264,33 @@ public void testGetFileTransferMetadatasGCS() throws Exception { Map expectedCreds = new HashMap<>(); - Assert.assertEquals(StageInfo.StageType.GCS, stageInfo.getStageType()); - Assert.assertEquals("foo/tables/9224/", stageInfo.getLocation()); - Assert.assertEquals(expectedCreds, stageInfo.getCredentials()); - Assert.assertEquals("US-WEST1", stageInfo.getRegion()); - Assert.assertEquals(null, stageInfo.getEndPoint()); - Assert.assertEquals(null, stageInfo.getStorageAccount()); - Assert.assertEquals(true, stageInfo.getIsClientSideEncrypted()); + assertEquals(StageInfo.StageType.GCS, stageInfo.getStageType()); + assertEquals("foo/tables/9224/", stageInfo.getLocation()); + assertEquals(expectedCreds, stageInfo.getCredentials()); + assertEquals("US-WEST1", stageInfo.getRegion()); + assertEquals(null, stageInfo.getEndPoint()); + assertEquals(null, stageInfo.getStorageAccount()); + assertEquals(true, stageInfo.getIsClientSideEncrypted()); assertEquals(Optional.empty(), stageInfo.gcsCustomEndpoint()); // EncryptionMaterial check - Assert.assertEquals("EXAMPLE_QUERY_ID", metadata.getEncryptionMaterial().getQueryId()); - Assert.assertEquals( + assertEquals("EXAMPLE_QUERY_ID", metadata.getEncryptionMaterial().getQueryId()); + assertEquals( "EXAMPLE_QUERY_STAGE_MASTER_KEY", metadata.getEncryptionMaterial().getQueryStageMasterKey()); - Assert.assertEquals(123L, (long) metadata.getEncryptionMaterial().getSmkId()); + assertEquals(123L, (long) metadata.getEncryptionMaterial().getSmkId()); // Misc check - Assert.assertEquals(SFBaseFileTransferAgent.CommandType.UPLOAD, metadata.getCommandType()); - Assert.assertEquals("EXAMPLE_PRESIGNED_URL", metadata.getPresignedUrl()); - Assert.assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); + assertEquals(SFBaseFileTransferAgent.CommandType.UPLOAD, metadata.getCommandType()); + assertEquals("EXAMPLE_PRESIGNED_URL", metadata.getPresignedUrl()); + assertEquals("orders_100.csv", metadata.getPresignedUrlFileName()); } @Test public void testGetFileTransferMetadataGCSWithUseRegionalUrl() throws Exception { List metadataList = SnowflakeFileTransferAgent.getFileTransferMetadatas(exampleGCSJsonNodeWithUseRegionalUrl); - Assert.assertEquals(1, metadataList.size()); + assertEquals(1, metadataList.size()); SnowflakeFileTransferMetadataV1 metadata = (SnowflakeFileTransferMetadataV1) metadataList.get(0); @@ -305,7 +305,7 @@ public void testGetFileTransferMetadataGCSWithUseRegionalUrl() throws Exception public void testGetFileTransferMetadataGCSWithEndPoint() throws Exception { List metadataList = SnowflakeFileTransferAgent.getFileTransferMetadatas(exampleGCSJsonNodeWithEndPoint); - Assert.assertEquals(1, metadataList.size()); + assertEquals(1, metadataList.size()); SnowflakeFileTransferMetadataV1 metadata = (SnowflakeFileTransferMetadataV1) metadataList.get(0); @@ -323,8 +323,8 @@ public void testGetFileTransferMetadatasUploadError() throws Exception { SnowflakeFileTransferAgent.getFileTransferMetadatas(downloadNode); fail(); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); - Assert.assertEquals( + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertEquals( "JDBC driver internal error: This API only supports PUT commands.", err.getMessage()); } } @@ -336,7 +336,7 @@ public void testGetFileTransferMetadatasEncryptionMaterialError() throws Excepti SnowflakeFileTransferAgent.getFileTransferMetadatas(garbageNode); fail(); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); assertTrue( err.getMessage().contains("JDBC driver internal error: Failed to parse the credentials")); } @@ -351,7 +351,7 @@ public void testGetFileTransferMetadatasUnsupportedLocationError() throws Except SnowflakeFileTransferAgent.getFileTransferMetadatas(modifiedNode); fail(); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); assertTrue(err.getMessage().contains("JDBC driver internal error: This API only supports")); } } @@ -363,7 +363,7 @@ public void testGetFileTransferMetadatasSrcLocationsArrayError() throws JsonProc SnowflakeFileTransferAgent.getFileTransferMetadatas(garbageNode); fail(); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); assertTrue( err.getMessage().contains("JDBC driver internal error: src_locations must be an array")); } @@ -378,7 +378,7 @@ public void testGetFileMetadatasEncryptionMaterialsException() { SnowflakeFileTransferAgent.getFileTransferMetadatas(modifiedNode); fail(); } catch (SnowflakeSQLException err) { - Assert.assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); + assertEquals((long) ErrorCode.INTERNAL_ERROR.getMessageCode(), err.getErrorCode()); assertTrue(err.getMessage().contains("Failed to parse encryptionMaterial")); } } diff --git a/src/test/java/net/snowflake/client/jdbc/GCPLargeResult.java b/src/test/java/net/snowflake/client/jdbc/GCPLargeResult.java index b2c316d50..44f9b7a48 100644 --- a/src/test/java/net/snowflake/client/jdbc/GCPLargeResult.java +++ b/src/test/java/net/snowflake/client/jdbc/GCPLargeResult.java @@ -3,41 +3,37 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) +@Tag(TestTags.RESULT_SET) public class GCPLargeResult extends BaseJDBCTest { - private final String queryResultFormat; - @Parameterized.Parameters(name = "format={0}") - public static Object[][] data() { - return new Object[][] {{"JSON"}, {"ARROW"}}; - } - - public GCPLargeResult(String queryResultFormat) { - this.queryResultFormat = queryResultFormat; - } - - Connection init() throws SQLException { + Connection init(String queryResultFormat) throws SQLException { Connection conn = BaseJDBCTest.getConnection("gcpaccount"); + System.out.println("Connected"); try (Statement stmt = conn.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); } return conn; } - @Test - public void testLargeResultSetGCP() throws Throwable { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testLargeResultSetGCP(String queryResultFormat) throws Throwable { + try (Connection con = init(queryResultFormat); PreparedStatement stmt = con.prepareStatement( "select seq8(), randstr(1000, random()) from table(generator(rowcount=>1000))")) { diff --git a/src/test/java/net/snowflake/client/jdbc/GitRepositoryDownloadLatestIT.java b/src/test/java/net/snowflake/client/jdbc/GitRepositoryDownloadLatestIT.java index b720591de..975f8ebb7 100644 --- a/src/test/java/net/snowflake/client/jdbc/GitRepositoryDownloadLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/GitRepositoryDownloadLatestIT.java @@ -3,9 +3,9 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.io.InputStream; @@ -17,14 +17,13 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import org.apache.commons.io.IOUtils; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class GitRepositoryDownloadLatestIT extends BaseJDBCTest { /** @@ -32,7 +31,7 @@ public class GitRepositoryDownloadLatestIT extends BaseJDBCTest { * accountadmin role. Added in > 3.19.0 */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void shouldDownloadFileAndStreamFromGitRepository() throws Exception { try (Connection connection = getConnection()) { prepareJdbcRepoInSnowflake(connection); @@ -48,8 +47,8 @@ public void shouldDownloadFileAndStreamFromGitRepository() throws Exception { List fetchedStreamContent = getContentFromStream(connection, stageName, filePathInGitRepo); - assertFalse("File content cannot be empty", fetchedFileContent.isEmpty()); - assertFalse("Stream content cannot be empty", fetchedStreamContent.isEmpty()); + assertFalse(fetchedFileContent.isEmpty(), "File content cannot be empty"); + assertFalse(fetchedStreamContent.isEmpty(), "Stream content cannot be empty"); assertEquals(fetchedFileContent, fetchedStreamContent); } } @@ -80,7 +79,7 @@ private static List getContentFromFile( try (Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(command); ) { // then - assertTrue("has result", rs.next()); + assertTrue(rs.next(), "has result"); return Files.readAllLines(downloadedFile); } finally { Files.delete(downloadedFile); diff --git a/src/test/java/net/snowflake/client/jdbc/HeartbeatAsyncLatestIT.java b/src/test/java/net/snowflake/client/jdbc/HeartbeatAsyncLatestIT.java index e7217f695..5dda21d55 100644 --- a/src/test/java/net/snowflake/client/jdbc/HeartbeatAsyncLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/HeartbeatAsyncLatestIT.java @@ -1,9 +1,9 @@ package net.snowflake.client.jdbc; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; @@ -12,18 +12,17 @@ import java.time.Duration; import java.util.Properties; import java.util.logging.Logger; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.QueryStatus; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * Test class for using heartbeat with asynchronous querying. This is a "Latest" class because old * driver versions do not contain the asynchronous querying API. */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class HeartbeatAsyncLatestIT extends HeartbeatIT { private static Logger logger = Logger.getLogger(HeartbeatAsyncLatestIT.class.getName()); @@ -69,20 +68,20 @@ protected void submitQuery(boolean useKeepAliveSession, int queryIdx) } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testAsynchronousQuerySuccess() throws Exception { testSuccess(); } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testAsynchronousQueryFailure() throws Exception { testFailure(); } /** Test that isValid() function returns false when session is expired */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testIsValidWithInvalidSession() throws Exception { try (Connection connection = getConnection()) { // assert that connection starts out valid diff --git a/src/test/java/net/snowflake/client/jdbc/HeartbeatIT.java b/src/test/java/net/snowflake/client/jdbc/HeartbeatIT.java index eb41ce76f..5f6d7867b 100644 --- a/src/test/java/net/snowflake/client/jdbc/HeartbeatIT.java +++ b/src/test/java/net/snowflake/client/jdbc/HeartbeatIT.java @@ -3,12 +3,13 @@ */ package net.snowflake.client.jdbc; +import static net.snowflake.client.AssumptionUtils.isRunningOnGithubActions; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; import java.sql.ResultSet; @@ -24,16 +25,15 @@ import java.util.concurrent.Future; import java.util.logging.Logger; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** This test assumes that GS has been set up */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class HeartbeatIT extends AbstractDriverIT { private static Logger logger = Logger.getLogger(HeartbeatIT.class.getName()); @@ -43,9 +43,9 @@ public class HeartbeatIT extends AbstractDriverIT { *

change the master token validity to 10 seconds change the session token validity to 5 * seconds change the SESSION_RECORD_ACCESS_INTERVAL_SECS to 1 second */ - @BeforeClass + @BeforeAll public static void setUpClass() throws Exception { - if (!RunningOnGithubAction.isRunningOnGithubAction()) { + if (!isRunningOnGithubActions()) { try (Connection connection = getSnowflakeAdminConnection(); Statement statement = connection.createStatement()) { statement.execute( @@ -61,9 +61,9 @@ public static void setUpClass() throws Exception { * Reset master_token_validity, session_token_validity, SESSION_RECORD_ACCESS_INTERVAL_SECS to * default. */ - @AfterClass + @AfterAll public static void tearDownClass() throws Exception { - if (!RunningOnGithubAction.isRunningOnGithubAction()) { + if (!isRunningOnGithubActions()) { try (Connection connection = getSnowflakeAdminConnection(); Statement statement = connection.createStatement()) { statement.execute( @@ -115,7 +115,7 @@ protected void submitQuery(boolean useKeepAliveSession, int queryIdx) * master token validity and issue a query to make sure the query succeeds. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testSuccess() throws Exception { int concurrency = 10; ExecutorService executorService = Executors.newFixedThreadPool(10); @@ -146,7 +146,7 @@ public void testSuccess() throws Exception { * master token validity and issue a query to make sure the query fails. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testFailure() throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(1); try { diff --git a/src/test/java/net/snowflake/client/jdbc/LobSizeLatestIT.java b/src/test/java/net/snowflake/client/jdbc/LobSizeLatestIT.java index 56f02c6d5..eb2bdfeb1 100644 --- a/src/test/java/net/snowflake/client/jdbc/LobSizeLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/LobSizeLatestIT.java @@ -3,8 +3,8 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; @@ -17,25 +17,26 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import net.snowflake.client.category.TestCategoryStatement; +import java.util.stream.Stream; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ObjectMapperFactory; import net.snowflake.client.core.UUIDUtils; import org.apache.commons.text.RandomStringGenerator; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -@Category(TestCategoryStatement.class) +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; + +@Tag(TestTags.STATEMENT) public class LobSizeLatestIT extends BaseJDBCTest { private static final Logger logger = Logger.getLogger(SnowflakeDriverIT.class.getName()); @@ -48,15 +49,16 @@ public class LobSizeLatestIT extends BaseJDBCTest { private static int smallLobSize = 16; private static int originLobSize = 16 * 1024 * 1024; - @BeforeClass + @BeforeAll public static void setUp() throws SQLException { - System.setProperty( - // the max json string should be ~1.33 for Arrow response so let's use 1.5 to be sure - ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM, Integer.toString((int) (maxLobSize * 1.5))); try (Connection con = BaseJDBCTest.getConnection()) { // get max LOB size from session maxLobSize = con.getMetaData().getMaxCharLiteralLength(); logger.log(Level.INFO, "Using max lob size: " + maxLobSize); + System.setProperty( + // the max json string should be ~1.33 for Arrow response so let's use 1.5 to be sure + ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM, + Integer.toString((int) (maxLobSize * 1.5))); LobSizeStringValues.put(smallLobSize, generateRandomString(smallLobSize)); LobSizeStringValues.put(originLobSize, generateRandomString(originLobSize)); LobSizeStringValues.put(mediumLobSize, generateRandomString(mediumLobSize)); @@ -65,31 +67,20 @@ public static void setUp() throws SQLException { } } - @Parameterized.Parameters(name = "lobSize={0}, resultFormat={1}") - public static Collection data() { - int[] lobSizes = - new int[] {smallLobSize, originLobSize, mediumLobSize, largeLobSize, maxLobSize}; - String[] resultFormats = new String[] {"Arrow", "JSON"}; - List ret = new ArrayList<>(); - for (int i = 0; i < lobSizes.length; i++) { - for (int j = 0; j < resultFormats.length; j++) { - ret.add(new Object[] {lobSizes[i], resultFormats[j]}); + static class DataProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + int[] lobSizes = + new int[] {smallLobSize, originLobSize, mediumLobSize, largeLobSize, maxLobSize}; + String[] resultFormats = new String[] {"Arrow", "JSON"}; + List ret = new ArrayList<>(); + for (int size : lobSizes) { + for (String format : resultFormats) { + ret.add(Arguments.of(size, format)); + } } - } - return ret; - } - - private final int lobSize; - - private final String resultFormat; - - public LobSizeLatestIT(int lobSize, String resultFormat) throws SQLException { - this.lobSize = lobSize; - this.resultFormat = resultFormat; - - try (Connection con = BaseJDBCTest.getConnection(); - Statement stmt = con.createStatement()) { - createTable(lobSize, stmt); + return ret.stream(); } } @@ -134,7 +125,7 @@ private void preparedInsertQuery(String varCharValue, String uuidValue, Connecti } } - @AfterClass + @AfterAll public static void tearDown() throws SQLException { try (Connection con = BaseJDBCTest.getConnection(); Statement stmt = con.createStatement()) { @@ -142,10 +133,13 @@ public static void tearDown() throws SQLException { } } - @Test - public void testStandardInsertAndSelectWithMaxLobSizeEnabled() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testStandardInsertAndSelectWithMaxLobSizeEnabled(int lobSize, String resultFormat) + throws SQLException { try (Connection con = BaseJDBCTest.getConnection(); Statement stmt = con.createStatement()) { + createTable(lobSize, stmt); setResultFormat(stmt, resultFormat); String varCharValue = LobSizeStringValues.get(lobSize); @@ -161,10 +155,13 @@ public void testStandardInsertAndSelectWithMaxLobSizeEnabled() throws SQLExcepti } } - @Test - public void testPreparedInsertWithMaxLobSizeEnabled() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testPreparedInsertWithMaxLobSizeEnabled(int lobSize, String resultFormat) + throws SQLException { try (Connection con = BaseJDBCTest.getConnection(); Statement stmt = con.createStatement()) { + createTable(lobSize, stmt); setResultFormat(stmt, resultFormat); String maxVarCharValue = LobSizeStringValues.get(lobSize); @@ -180,8 +177,9 @@ public void testPreparedInsertWithMaxLobSizeEnabled() throws SQLException { } } - @Test - public void testPutAndGet() throws IOException, SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testPutAndGet(int lobSize, String resultFormat) throws IOException, SQLException { File tempFile = File.createTempFile("LobSizeTest", ".csv"); // Delete file when JVM shuts down tempFile.deleteOnExit(); @@ -201,6 +199,7 @@ public void testPutAndGet() throws IOException, SQLException { try (Connection con = BaseJDBCTest.getConnection(); Statement stmt = con.createStatement()) { + createTable(lobSize, stmt); setResultFormat(stmt, resultFormat); if (lobSize > originLobSize) { // for increased LOB size (16MB < lobSize < 128MB) stmt.execute("alter session set ALLOW_LARGE_LOBS_IN_EXTERNAL_SCAN = true"); diff --git a/src/test/java/net/snowflake/client/jdbc/MaxLobSizeLatestIT.java b/src/test/java/net/snowflake/client/jdbc/MaxLobSizeLatestIT.java index fd2957528..17afeeb53 100644 --- a/src/test/java/net/snowflake/client/jdbc/MaxLobSizeLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/MaxLobSizeLatestIT.java @@ -4,20 +4,19 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.text.IsEmptyString.emptyOrNullString; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryStatement; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import org.hamcrest.CoreMatchers; -import org.junit.Assert; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class MaxLobSizeLatestIT extends BaseJDBCTest { /** @@ -26,7 +25,7 @@ public class MaxLobSizeLatestIT extends BaseJDBCTest { * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testIncreasedMaxLobSize() throws SQLException { try (Connection con = BaseJDBCTest.getConnection(); Statement stmt = con.createStatement()) { @@ -41,7 +40,7 @@ public void testIncreasedMaxLobSize() throws SQLException { stmt.execute("alter session set ENABLE_LARGE_VARCHAR_AND_BINARY_IN_RESULT=true"); try (ResultSet resultSet = stmt.executeQuery("select randstr(20000000, random()) as large_str")) { - Assert.assertTrue(resultSet.next()); + assertTrue(resultSet.next()); assertThat(resultSet.getString(1), is(not(emptyOrNullString()))); } finally { stmt.execute("alter session unset ENABLE_LARGE_VARCHAR_AND_BINARY_IN_RESULT"); diff --git a/src/test/java/net/snowflake/client/jdbc/MockConnectionTest.java b/src/test/java/net/snowflake/client/jdbc/MockConnectionTest.java index c763606fe..65118cec6 100644 --- a/src/test/java/net/snowflake/client/jdbc/MockConnectionTest.java +++ b/src/test/java/net/snowflake/client/jdbc/MockConnectionTest.java @@ -1,8 +1,8 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -31,7 +31,6 @@ import java.util.concurrent.Future; import java.util.stream.Collectors; import java.util.stream.IntStream; -import net.snowflake.client.category.TestCategoryConnection; import net.snowflake.client.core.ExecTimeTelemetryData; import net.snowflake.client.core.ParameterBindingDTO; import net.snowflake.client.core.QueryContextDTO; @@ -52,15 +51,15 @@ import net.snowflake.client.jdbc.telemetry.TelemetryData; import net.snowflake.common.core.SFBinaryFormat; import net.snowflake.common.core.SnowflakeDateTimeFormat; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Test; /** * IT test for testing the "pluggable" implementation of SnowflakeConnection, SnowflakeStatement, * and ResultSet. These tests will query Snowflake normally, retrieve the JSON result, and replay it * back using a custom implementation of these objects that simply echoes a given JSON response. */ -@Category(TestCategoryConnection.class) +// TODO: SNOW-1821554 +// @Tag(TestTags.CONNECTION) public class MockConnectionTest extends BaseJDBCTest { // Simple pair class container for the error test. @@ -277,7 +276,7 @@ public void testMockResponse() throws SQLException, JsonProcessingException { mockConnection.prepareStatement("select count(*) from " + testTableName).executeQuery(); fakeResultSet.next(); String val = fakeResultSet.getString(1); - assertEquals("colA value from the mock connection was not what was expected", "rowOne", val); + assertEquals("rowOne", val, "colA value from the mock connection was not what was expected"); mockConnection.close(); } @@ -411,7 +410,7 @@ public void testMockTransferAgent() throws SQLException, IOException { InputStream downloadStream1 = mockConnection.downloadStream("@fakeStage", "file1", false); byte[] outputBytes1 = new byte[downloadStream1.available()]; downloadStream1.read(outputBytes1); - assertArrayEquals("downloaded bytes not what was expected", outputBytes1, inputBytes1); + assertArrayEquals(outputBytes1, inputBytes1, "downloaded bytes not what was expected"); } private JsonNode createDummyResponseWithRows(List> rows, List dataTypes) { @@ -540,7 +539,7 @@ private void compareResultSets( resultSetRows++; } - assertEquals("row-count was not what was expected", numRows, resultSetRows); + assertEquals(numRows, resultSetRows, "row-count was not what was expected"); } // DataTypes supported with mock responses in test: diff --git a/src/test/java/net/snowflake/client/jdbc/MultiStatementArrowIT.java b/src/test/java/net/snowflake/client/jdbc/MultiStatementArrowIT.java index 0a1fb9ce2..5ea3b3f27 100644 --- a/src/test/java/net/snowflake/client/jdbc/MultiStatementArrowIT.java +++ b/src/test/java/net/snowflake/client/jdbc/MultiStatementArrowIT.java @@ -1,9 +1,9 @@ package net.snowflake.client.jdbc; -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; -@Category(TestCategoryArrow.class) +@Tag(TestTags.ARROW) public class MultiStatementArrowIT extends MultiStatementIT { public MultiStatementArrowIT() { diff --git a/src/test/java/net/snowflake/client/jdbc/MultiStatementIT.java b/src/test/java/net/snowflake/client/jdbc/MultiStatementIT.java index 06ccc4196..f4e9da56d 100644 --- a/src/test/java/net/snowflake/client/jdbc/MultiStatementIT.java +++ b/src/test/java/net/snowflake/client/jdbc/MultiStatementIT.java @@ -3,33 +3,31 @@ */ package net.snowflake.client.jdbc; -import static net.snowflake.client.ConditionalIgnoreRule.ConditionalIgnore; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryStatement; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.SFSession; import net.snowflake.common.core.SqlState; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Multi Statement tests */ -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class MultiStatementIT extends BaseJDBCWithSharedConnectionIT { protected static String queryResultFormat = "json"; - @Before + @BeforeEach public void setQueryResultFormat() throws SQLException { try (Statement stmt = connection.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); @@ -418,7 +416,7 @@ public void testMultiStmtCountNotMatch() throws SQLException { } @Test - @ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testInvalidParameterCount() throws SQLException { String userName = null; String accountName = null; @@ -455,7 +453,7 @@ public void testInvalidParameterCount() throws SQLException { for (int i = 0; i < testSuites.length; i++) { try { statement.execute(testSuites[i]); - Assert.fail(); + fail(); } catch (SQLException e) { assertThat(e.getErrorCode(), is(expectedErrorCodes[i])); } diff --git a/src/test/java/net/snowflake/client/jdbc/MultiStatementLatestIT.java b/src/test/java/net/snowflake/client/jdbc/MultiStatementLatestIT.java index eedf56114..849742ef9 100644 --- a/src/test/java/net/snowflake/client/jdbc/MultiStatementLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/MultiStatementLatestIT.java @@ -3,19 +3,19 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * MultiStatement integration tests for the latest JDBC driver. This doesn't work for the oldest @@ -23,11 +23,11 @@ * if the tests still is not applicable. If it is applicable, move tests to MultiStatementIT so that * both the latest and oldest supported driver run the tests. */ -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class MultiStatementLatestIT extends BaseJDBCWithSharedConnectionIT { protected static String queryResultFormat = "json"; - @Before + @BeforeEach public void setQueryResultFormat() throws SQLException { try (Statement stmt = connection.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); diff --git a/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncIT.java b/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncIT.java index d68dc8fc5..f8ca2f48a 100644 --- a/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncIT.java +++ b/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncIT.java @@ -3,24 +3,24 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Test OpenGroup CLI */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class OpenGroupCLIFuncIT extends BaseJDBCWithSharedConnectionIT { - @BeforeClass + @BeforeAll public static void setSessionTimezone() throws SQLException { try (Statement statement = connection.createStatement()) { statement.execute( diff --git a/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncLatestIT.java b/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncLatestIT.java index 4f7004004..3b79dc616 100644 --- a/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/OpenGroupCLIFuncLatestIT.java @@ -7,9 +7,9 @@ import java.sql.Connection; import java.sql.SQLException; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * Open Group CLI function integration tests for the latest JDBC driver. This doesn't work for the @@ -17,7 +17,7 @@ * examine if the tests still are not applicable. If it is applicable, move tests to * OpenGroupCLIFuncIT so that both the latest and oldest supported driver run the tests. */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class OpenGroupCLIFuncLatestIT extends BaseJDBCTest { /** * Numeric function tests diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtIT.java index 224c538d0..adb92036d 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtIT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedMultiStmtIT.java @@ -3,47 +3,37 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -@Category(TestCategoryStatement.class) -public class PreparedMultiStmtIT extends BaseJDBCWithSharedConnectionIT { - - @Parameterized.Parameters(name = "format={0}") - public static Object[][] data() { - // all tests in this class need to run for both query result formats json and arrow - return new Object[][] {{"JSON"}, {"Arrow"}}; - } +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; - protected String queryResultFormat; +@Tag(TestTags.STATEMENT) +public class PreparedMultiStmtIT extends BaseJDBCWithSharedConnectionIT { private static SnowflakeConnectionV1 sfConnectionV1; - public PreparedMultiStmtIT(String queryResultFormat) { - this.queryResultFormat = queryResultFormat; + public PreparedMultiStmtIT() { this.sfConnectionV1 = (SnowflakeConnectionV1) connection; } - @Before - public void setSessionResultFormat() throws SQLException { + public void setSessionResultFormat(String queryResultFormat) throws SQLException { try (Statement stmt = connection.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); } } - @Test - public void testExecuteUpdateCount() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testExecuteUpdateCount(String queryResultFormat) throws Exception { + setSessionResultFormat(queryResultFormat); try (Statement statement = sfConnectionV1.createStatement()) { try { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); @@ -87,8 +77,10 @@ public void testExecuteUpdateCount() throws Exception { } /** Less bindings than expected in statement */ - @Test - public void testExecuteLessBindings() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testExecuteLessBindings(String queryResultFormat) throws Exception { + setSessionResultFormat(queryResultFormat); try (Statement statement = sfConnectionV1.createStatement()) { try { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); @@ -107,7 +99,7 @@ public void testExecuteLessBindings() throws Exception { // first statement try { preparedStatement.executeUpdate(); - Assert.fail(); + fail(); } catch (SQLException e) { // error code comes from xp, which is js execution failed. assertThat(e.getErrorCode(), is(100132)); @@ -119,8 +111,10 @@ public void testExecuteLessBindings() throws Exception { } } - @Test - public void testExecuteMoreBindings() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testExecuteMoreBindings(String queryResultFormat) throws Exception { + setSessionResultFormat(queryResultFormat); try (Statement statement = sfConnectionV1.createStatement()) { try { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); @@ -165,8 +159,10 @@ public void testExecuteMoreBindings() throws Exception { } } - @Test - public void testExecuteQueryBindings() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testExecuteQueryBindings(String queryResultFormat) throws Exception { + setSessionResultFormat(queryResultFormat); try (Statement statement = sfConnectionV1.createStatement()) { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); @@ -207,8 +203,10 @@ public void testExecuteQueryBindings() throws Exception { } } - @Test - public void testExecuteQueryNoBindings() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testExecuteQueryNoBindings(String queryResultFormat) throws Exception { + setSessionResultFormat(queryResultFormat); try (Statement statement = sfConnectionV1.createStatement()) { statement.execute("alter session set MULTI_STATEMENT_COUNT=0"); diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatement0IT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatement0IT.java index 7c05163dc..aa9a90859 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatement0IT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedStatement0IT.java @@ -6,14 +6,16 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; -import org.junit.After; -import org.junit.Before; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; /** Prepared statement integration tests */ abstract class PreparedStatement0IT extends BaseJDBCTest { - private final String queryResultFormat; - Connection init() throws SQLException { + return BaseJDBCTest.getConnection(); + } + + protected Connection getConn(String queryResultFormat) throws SQLException { Connection conn = BaseJDBCTest.getConnection(); try (Statement stmt = conn.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); @@ -34,21 +36,17 @@ Connection init() throws SQLException { final String enableCacheReuse = "alter session set USE_CACHED_RESULT=true"; final String tableFuncSQL = "select 1 from table(generator(rowCount => ?))"; - @Before + @BeforeEach public void setUp() throws SQLException { try (Connection con = init()) { con.createStatement().execute(createTableSQL); } } - @After + @AfterEach public void tearDown() throws SQLException { try (Connection con = init()) { con.createStatement().execute(deleteTableSQL); } } - - PreparedStatement0IT(String queryResultFormat) { - this.queryResultFormat = queryResultFormat; - } } diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatement1IT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatement1IT.java index 56bef419f..d0074230d 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatement1IT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedStatement1IT.java @@ -6,12 +6,12 @@ import static net.snowflake.client.jdbc.ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; import java.sql.DriverManager; @@ -25,26 +25,22 @@ import java.sql.Types; import java.util.Map; import java.util.Properties; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -@Category(TestCategoryStatement.class) +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +@Tag(TestTags.STATEMENT) public class PreparedStatement1IT extends PreparedStatement0IT { - public PreparedStatement1IT() { - super("json"); - } - - PreparedStatement1IT(String queryFormat) { - super(queryFormat); - } - @Test - public void testGetParameterMetaData() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetParameterMetaData(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement preparedStatement = connection.prepareStatement(updateSQL)) { /* All binding parameters are of type text and have null precision and scale and are not nullable. Since every binding parameter currently has identical properties, testing is minimal until this changes. @@ -83,9 +79,10 @@ public void testGetParameterMetaData() throws SQLException { } /** Trigger default stage array binding threshold so that it can be run on travis */ - @Test - public void testInsertStageArrayBind() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testInsertStageArrayBind(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { connection .createStatement() @@ -122,9 +119,10 @@ static void bindOneParamSet( prepst.setShort(6, colE); } - @Test - public void testPrepareStatementWithKeys() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareStatementWithKeys(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { connection.createStatement().execute(createTableSQL); try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL, Statement.NO_GENERATED_KEYS)) { @@ -138,11 +136,12 @@ public void testPrepareStatementWithKeys() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testInsertBatch() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testInsertBatch(String queryResultFormat) throws SQLException { int[] countResult; - try (Connection connection = init()) { + try (Connection connection = getConn(queryResultFormat)) { connection .createStatement() .execute( @@ -164,11 +163,12 @@ public void testInsertBatch() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testInsertBatchStage() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testInsertBatchStage(String queryResultFormat) throws SQLException { int[] countResult; - try (Connection connection = init()) { + try (Connection connection = getConn(queryResultFormat)) { connection .createStatement() .execute( @@ -188,12 +188,13 @@ public void testInsertBatchStage() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testInsertBatchStageMultipleTimes() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testInsertBatchStageMultipleTimes(String queryResultFormat) throws SQLException { // using the same statement to run a query multiple times shouldn't result in duplicates int[] countResult; - try (Connection connection = init()) { + try (Connection connection = getConn(queryResultFormat)) { connection .createStatement() .execute( @@ -223,10 +224,11 @@ public void testInsertBatchStageMultipleTimes() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testStageBatchNull() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testStageBatchNull(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { int[] thresholds = {0, 6}; // disabled, enabled @@ -253,26 +255,27 @@ public void testStageBatchNull() throws SQLException { String errorMessage = "Column should be null (" + (threshold > 0 ? "stage" : "non-stage") + ")"; resultSet.getInt(1); - assertTrue(errorMessage, resultSet.wasNull()); + assertTrue(resultSet.wasNull(), errorMessage); resultSet.getDouble(2); - assertTrue(errorMessage, resultSet.wasNull()); + assertTrue(resultSet.wasNull(), errorMessage); resultSet.getFloat(3); - assertTrue(errorMessage, resultSet.wasNull()); + assertTrue(resultSet.wasNull(), errorMessage); resultSet.getString(4); - assertTrue(errorMessage, resultSet.wasNull()); + assertTrue(resultSet.wasNull(), errorMessage); resultSet.getLong(5); - assertTrue(errorMessage, resultSet.wasNull()); + assertTrue(resultSet.wasNull(), errorMessage); resultSet.getShort(6); - assertTrue(errorMessage, resultSet.wasNull()); + assertTrue(resultSet.wasNull(), errorMessage); } } } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testStageString() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testStageString(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { int[] thresholds = {0, 6}; // disabled, enabled String[] rows = { @@ -297,7 +300,7 @@ public void testStageString() throws SQLException { "Strings should match (" + (threshold > 0 ? "stage" : "non-stage") + ")"; for (String row : rows) { assertTrue(resultSet.next()); - assertEquals(errorMessage, row, resultSet.getString(1)); + assertEquals(row, resultSet.getString(1), errorMessage); } } } @@ -305,10 +308,11 @@ public void testStageString() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testIncorrectTypes() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testIncorrectTypes(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { int[] thresholds = {0, 6}; // disabled, enabled @@ -338,10 +342,11 @@ public void testIncorrectTypes() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testStageBatchTimestamps() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testStageBatchTimestamps(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { Timestamp tsEpoch = new Timestamp(0L); Timestamp tsEpochMinusOneSec = new Timestamp(-1000L); // negative epoch no fraction of seconds @@ -409,11 +414,11 @@ public void testStageBatchTimestamps() throws SQLException { for (int i = 0; i < timestamps.length; i++) { assertEquals( + nonStageResult[i], + stageResult[i], "Stage binding timestamp should match non-stage binding timestamp (" + tsType - + ")", - nonStageResult[i], - stageResult[i]); + + ")"); } } } @@ -424,10 +429,11 @@ public void testStageBatchTimestamps() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testStageBatchTimes() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testStageBatchTimes(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { Time tMidnight = new Time(0); Time tNeg = new Time(-1); @@ -487,9 +493,9 @@ public void testStageBatchTimes() throws SQLException { for (int i = 0; i < times.length; i++) { assertEquals( - "Stage binding time should match non-stage binding time", nonStageResult[i], - stageResult[i]); + stageResult[i], + "Stage binding time should match non-stage binding time"); } } } @@ -499,9 +505,10 @@ public void testStageBatchTimes() throws SQLException { } } - @Test - public void testClearParameters() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testClearParameters(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { bindOneParamSet(prepStatement, 1, 1.22222, (float) 1.2, "test", 12121212121L, (short) 12); prepStatement.clearParameters(); @@ -522,9 +529,10 @@ public void testClearParameters() throws SQLException { } } - @Test - public void testClearBatch() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testClearBatch(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { bindOneParamSet(prepStatement, 1, 1.22222, (float) 1.2, "test", 12121212121L, (short) 12); prepStatement.addBatch(); @@ -555,9 +563,10 @@ public void testClearBatch() throws SQLException { } } - @Test - public void testInsertOneRow() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testInsertOneRow(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute("CREATE OR REPLACE TABLE test_prepst_date (id INTEGER, d DATE)"); try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { @@ -576,9 +585,10 @@ public void testInsertOneRow() throws SQLException { } } - @Test - public void testUpdateOneRow() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testUpdateOneRow(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute("CREATE OR REPLACE TABLE test_prepst_date (id INTEGER, d DATE)"); try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { @@ -611,9 +621,10 @@ public void testUpdateOneRow() throws SQLException { } } - @Test - public void testDeleteOneRow() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testDeleteOneRow(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute("CREATE OR REPLACE TABLE test_prepst_date (id INTEGER, d DATE)"); try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { @@ -654,9 +665,10 @@ public void testDeleteOneRow() throws SQLException { } } - @Test - public void testSelectOneRow() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSelectOneRow(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { bindOneParamSet(prepStatement, 1, 1.22222, (float) 1.2, "test", 12121212121L, (short) 12); prepStatement.addBatch(); @@ -680,9 +692,10 @@ public void testSelectOneRow() throws SQLException { } } - @Test - public void testUpdateBatch() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testUpdateBatch(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { bindOneParamSet(prepStatement, 1, 1.22222, (float) 1.2, "test", 12121212121L, (short) 12); prepStatement.addBatch(); @@ -715,10 +728,11 @@ public void testUpdateBatch() throws SQLException { } } - @Test - public void testBatchInsertWithCacheEnabled() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testBatchInsertWithCacheEnabled(String queryResultFormat) throws SQLException { int[] countResult; - try (Connection connection = init(); + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { // ensure enable the cache result use statement.execute(enableCacheReuse); @@ -764,7 +778,7 @@ public void testBatchInsertWithCacheEnabled() throws SQLException { * @throws SQLException arises if any exception occurs */ @Test - @Ignore + @Disabled public void manualTestForPreparedStatementLogging() throws SQLException { Map params = getConnectionParameters(); Properties props = new Properties(); diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatement1LatestIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatement1LatestIT.java index 872c8aab6..9c316edba 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatement1LatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedStatement1LatestIT.java @@ -4,10 +4,10 @@ package net.snowflake.client.jdbc; import static net.snowflake.client.jdbc.PreparedStatement1IT.bindOneParamSet; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.math.BigInteger; import java.sql.Connection; @@ -17,12 +17,14 @@ import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import java.util.TimeZone; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** * PreparedStatement integration tests for the latest JDBC driver. This doesn't work for the oldest @@ -30,19 +32,13 @@ * if the tests still are not applicable. If it is applicable, move tests to PreparedStatement1IT so * that both the latest and oldest supported driver run the tests. */ -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class PreparedStatement1LatestIT extends PreparedStatement0IT { - public PreparedStatement1LatestIT() { - super("json"); - } - - PreparedStatement1LatestIT(String queryResultFormat) { - super(queryResultFormat); - } - @Test - public void testPrepStWithCacheEnabled() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepStWithCacheEnabled(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { // ensure enable the cache result use statement.execute(enableCacheReuse); @@ -107,10 +103,13 @@ public void testPrepStWithCacheEnabled() throws SQLException { * * @throws SQLException arises if any exception occurs */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testInsertStageArrayBindWithTime() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testInsertStageArrayBindWithTime(String queryResultFormat) throws SQLException { + TimeZone originalTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("alter session set CLIENT_STAGE_ARRAY_BINDING_THRESHOLD=2"); @@ -140,6 +139,7 @@ public void testInsertStageArrayBindWithTime() throws SQLException { } finally { statement.execute("drop table if exists testStageBindTime"); statement.execute("alter session unset CLIENT_STAGE_ARRAY_BINDING_THRESHOLD"); + TimeZone.setDefault(originalTimeZone); } } } @@ -154,10 +154,11 @@ public void testInsertStageArrayBindWithTime() throws SQLException { * * @throws SQLException */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testSetObjectForTimestampTypes() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testSetObjectForTimestampTypes(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { // set timestamp mapping to default value try { @@ -210,14 +211,15 @@ public void testSetObjectForTimestampTypes() throws SQLException { * * @throws SQLException arises if any exception occurs */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testExecuteEmptyBatch() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testExecuteEmptyBatch(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { // executeBatch shouldn't throw exceptions assertEquals( - "For empty batch, we should return int[0].", 0, prepStatement.executeBatch().length); + 0, prepStatement.executeBatch().length, "For empty batch, we should return int[0]."); } connection @@ -228,7 +230,7 @@ public void testExecuteEmptyBatch() throws SQLException { try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { // executeBatch shouldn't throw exceptions assertEquals( - "For empty batch, we should return int[0].", 0, prepStatement.executeBatch().length); + 0, prepStatement.executeBatch().length, "For empty batch, we should return int[0]."); } } } @@ -238,9 +240,10 @@ public void testExecuteEmptyBatch() throws SQLException { * * @throws SQLException */ - @Test - public void testSetObjectMethodWithVarbinaryColumn() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSetObjectMethodWithVarbinaryColumn(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { connection.createStatement().execute("create or replace table test_binary(b VARBINARY)"); try (PreparedStatement prepStatement = @@ -251,16 +254,17 @@ public void testSetObjectMethodWithVarbinaryColumn() throws SQLException { } } - @Test - public void testSetObjectMethodWithBigIntegerColumn() { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSetObjectMethodWithBigIntegerColumn(String queryResultFormat) { + try (Connection connection = getConn(queryResultFormat)) { connection.createStatement().execute("create or replace table test_bigint(id NUMBER)"); try (PreparedStatement prepStatement = connection.prepareStatement("insert into test_bigint(id) values(?)")) { prepStatement.setObject(1, BigInteger.valueOf(9999)); int rows = prepStatement.executeUpdate(); - assertTrue("Row count doesn't match", rows == 1); + assertTrue(rows == 1, "Row count doesn't match"); } } catch (SQLException e) { e.printStackTrace(); @@ -270,9 +274,10 @@ public void testSetObjectMethodWithBigIntegerColumn() { } } - @Test - public void testSetObjectMethodWithLargeBigIntegerColumn() { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSetObjectMethodWithLargeBigIntegerColumn(String queryResultFormat) { + try (Connection connection = getConn(queryResultFormat)) { connection.createStatement().execute("create or replace table test_bigint(id NUMBER)"); try (PreparedStatement prepStatement = @@ -280,7 +285,7 @@ public void testSetObjectMethodWithLargeBigIntegerColumn() { BigInteger largeBigInt = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.TEN); prepStatement.setObject(1, largeBigInt); int rows = prepStatement.executeUpdate(); - assertTrue("Row count doesn't match", rows == 1); + assertTrue(rows == 1, "Row count doesn't match"); } } catch (SQLException e) { e.printStackTrace(); @@ -290,9 +295,13 @@ public void testSetObjectMethodWithLargeBigIntegerColumn() { } } - @Test - public void testBatchInsertWithTimestampInputFormatSet() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testBatchInsertWithTimestampInputFormatSet(String queryResultFormat) + throws SQLException { + TimeZone originalTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("alter session set TIMESTAMP_INPUT_FORMAT='YYYY-MM-DD HH24:MI:SS.FFTZH'"); @@ -315,6 +324,8 @@ public void testBatchInsertWithTimestampInputFormatSet() throws SQLException { statement.execute("drop table if exists testStageBindTypes"); statement.execute("alter session unset TIMESTAMP_INPUT_FORMAT"); } + } finally { + TimeZone.setDefault(originalTimeZone); } } @@ -324,10 +335,11 @@ public void testBatchInsertWithTimestampInputFormatSet() throws SQLException { * * @throws SQLException */ - @Test - @Ignore - public void testCallStatement() throws SQLException { - try (Connection connection = getConnection(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @Disabled + public void testCallStatement(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.executeQuery( diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatement2IT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatement2IT.java index efb8ef944..96765131a 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatement2IT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedStatement2IT.java @@ -7,12 +7,12 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.Sets; import java.math.BigDecimal; @@ -28,27 +28,21 @@ import java.sql.Timestamp; import java.util.Calendar; import java.util.Set; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Assert; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -@Category(TestCategoryStatement.class) +import java.util.TimeZone; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +@Tag(TestTags.STATEMENT) public class PreparedStatement2IT extends PreparedStatement0IT { - public PreparedStatement2IT() { - super("json"); - } - - PreparedStatement2IT(String queryFormat) { - super(queryFormat); - } - - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testStageBatchDates() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testStageBatchDates(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { Date dEpoch = new Date(0); Date dAfterEpoch = new Date(24 * 60 * 60 * 1000); @@ -111,9 +105,9 @@ public void testStageBatchDates() throws SQLException { for (int i = 0; i < dates.length; i++) { assertEquals( - "Stage binding date should match non-stage binding date", nonStageResult[i], - stageResult[i]); + stageResult[i], + "Stage binding date should match non-stage binding date"); } } } @@ -123,9 +117,10 @@ public void testStageBatchDates() throws SQLException { } } - @Test - public void testBindWithNullValue() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testBindWithNullValue(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute( "create or replace table testBindNull(cola date, colb time, colc timestamp, cold number)"); @@ -183,9 +178,10 @@ public void testBindWithNullValue() throws SQLException { } } - @Test - public void testPrepareDDL() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareDDL(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { try (PreparedStatement prepStatement = @@ -203,9 +199,10 @@ public void testPrepareDDL() throws SQLException { } } - @Test - public void testPrepareSCL() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareSCL(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement("use SCHEMA PUBLIC")) { prepStatement.execute(); } @@ -217,9 +214,10 @@ public void testPrepareSCL() throws SQLException { } } - @Test - public void testPrepareTCL() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareTCL(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { connection.setAutoCommit(false); String[] testCases = {"BEGIN", "COMMIT"}; @@ -234,9 +232,10 @@ public void testPrepareTCL() throws SQLException { } } - @Test - public void testPrepareShowCommand() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareShowCommand(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement("show databases")) { try (ResultSet resultSet = prepStatement.executeQuery()) { assertTrue(resultSet.next()); @@ -253,14 +252,16 @@ public void testPrepareShowCommand() throws SQLException { * @throws SQLException Will be thrown if any of driver calls fail * @throws InterruptedException Will be thrown if the sleep is interrupted */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testPrepareTimeout() throws SQLException, InterruptedException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testPrepareTimeout(String queryResultFormat) + throws SQLException, InterruptedException { try (Connection adminCon = getSnowflakeAdminConnection(); Statement adminStatement = adminCon.createStatement()) { adminStatement.execute("alter system set enable_combined_describe=true"); try { - try (Connection connection = init(); + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute("create or replace table t(c1 string) as select 1"); statement.execute("alter session set jdbc_enable_combined_describe=true"); @@ -281,11 +282,12 @@ public void testPrepareTimeout() throws SQLException, InterruptedException { } /** Test case to make sure 2 non null bind refs was not constant folded into one */ - @Test - public void testSnow36284() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSnow36284(String queryResultFormat) throws Exception { String query = "select * from (values ('a'), ('b')) x where x.COLUMN1 in (?,?);"; - try (Connection connection = init(); + try (Connection connection = getConn(queryResultFormat); PreparedStatement preparedStatement = connection.prepareStatement(query)) { preparedStatement.setString(1, "a"); preparedStatement.setString(2, "b"); @@ -296,17 +298,18 @@ public void testSnow36284() throws Exception { rowcount++; valuesReturned.add(rs.getString(1)); } - assertEquals("Should get back 2 rows", 2, rowcount); - assertEquals("", valuesReturned, Sets.newHashSet("a", "b")); + assertEquals(2, rowcount, "Should get back 2 rows"); + assertEquals(valuesReturned, Sets.newHashSet("a", "b"), ""); } } } /** Test for coalesce with bind and null arguments in a prepared statement */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testSnow35923() throws Exception { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testSnow35923(String queryResultFormat) throws Exception { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute( "alter session set " + "optimizer_eliminate_scans_for_constant_select=false"); @@ -325,14 +328,15 @@ public void testSnow35923() throws Exception { * Tests binding of object literals, including binding with object names as well as binding with * object IDs */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testBindObjectLiteral() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testBindObjectLiteral(String queryResultFormat) throws Exception { long t1Id = 0; long t2Id = 0; String t1 = null; - try (Connection conn = init(); + try (Connection conn = getConn(queryResultFormat); Statement stmt = conn.createStatement()) { String sqlText = "create or replace table identifier(?) (c1 number)"; @@ -480,9 +484,10 @@ public void testBindObjectLiteral() throws Exception { } } - @Test - public void testBindTimestampTZViaString() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testBindTimestampTZViaString(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute( @@ -509,16 +514,23 @@ public void testBindTimestampTZViaString() throws SQLException { * Ensures binding a string type with TIMESTAMP_TZ works. The customer has to use the specific * timestamp format: YYYY-MM-DD HH24:MI:SS.FF9 TZH:TZM */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testBindTimestampTZViaStringBatch() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testBindTimestampTZViaStringBatch(String queryResultFormat) throws SQLException { + TimeZone originalTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute( "ALTER SESSION SET CLIENT_STAGE_ARRAY_BINDING_THRESHOLD = 1"); // enable stage bind statement.execute( "create or replace table testbindtstz(cola timestamp_tz, colb timestamp_ntz)"); + statement.execute( + "ALTER SESSION SET TIMESTAMP_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'"); + statement.execute( + "ALTER SESSION SET TIMESTAMP_NTZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'"); try (PreparedStatement preparedStatement = connection.prepareStatement("insert into testbindtstz values(?,?)")) { @@ -546,6 +558,8 @@ public void testBindTimestampTZViaStringBatch() throws SQLException { } finally { statement.execute("drop table if exists testbindtstz"); } + } finally { + TimeZone.setDefault(originalTimeZone); } } @@ -555,9 +569,10 @@ public void testBindTimestampTZViaStringBatch() throws SQLException { * * @throws Exception raises if any error occurs */ - @Test - public void testSnow41620() throws Exception { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSnow41620(String queryResultFormat) throws Exception { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { // Create a table and insert 3 records statement.execute("CREATE or REPLACE TABLE SNOW41620 (c1 varchar(20)," + "c2 int" + " )"); @@ -592,9 +607,10 @@ public void testSnow41620() throws Exception { } } - @Test - public void testSnow50141() throws Exception { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSnow50141(String queryResultFormat) throws Exception { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement("select 1 where true=?")) { prepStatement.setObject(1, true); try (ResultSet resultSet = prepStatement.executeQuery()) { @@ -617,9 +633,9 @@ public void testSnow50141() throws Exception { private void checkResultSetEqual(ResultSet rs1, ResultSet rs2) throws SQLException { int columns = rs1.getMetaData().getColumnCount(); assertEquals( - "Resultsets do not match in the number of columns returned", columns, - rs2.getMetaData().getColumnCount()); + rs2.getMetaData().getColumnCount(), + "Resultsets do not match in the number of columns returned"); while (rs1.next() && rs2.next()) { for (int columnIndex = 1; columnIndex <= columns; columnIndex++) { @@ -627,19 +643,20 @@ private void checkResultSetEqual(ResultSet rs1, ResultSet rs2) throws SQLExcepti final Object res2 = rs2.getObject(columnIndex); assertEquals( - String.format("%s and %s are not equal values at column %d", res1, res2, columnIndex), res1, - res2); + res2, + String.format("%s and %s are not equal values at column %d", res1, res2, columnIndex)); } assertEquals( - "Number of records returned by the results does not match", rs1.isLast(), rs2.isLast()); + rs1.isLast(), rs2.isLast(), "Number of records returned by the results does not match"); } } - @Test - public void testPreparedStatementWithSkipParsing() throws Exception { - try (Connection con = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPreparedStatementWithSkipParsing(String queryResultFormat) throws Exception { + try (Connection con = getConn(queryResultFormat)) { PreparedStatement stmt = con.unwrap(SnowflakeConnectionV1.class).prepareStatement("select 1", true); try (ResultSet rs = stmt.executeQuery()) { @@ -649,9 +666,11 @@ public void testPreparedStatementWithSkipParsing() throws Exception { } } - @Test - public void testPreparedStatementWithSkipParsingAndBinding() throws Exception { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPreparedStatementWithSkipParsingAndBinding(String queryResultFormat) + throws Exception { + try (Connection con = getConn(queryResultFormat); Statement statement = con.createStatement()) { statement.execute("create or replace table t(c1 int)"); try { @@ -679,9 +698,10 @@ public void testPreparedStatementWithSkipParsingAndBinding() throws Exception { * workaround is added. More specifically, ErrorCode returned for this statement is caught in * SnowflakePreparedStatementV1 so that execution can continue */ - @Test - public void testSnow44393() throws Exception { - try (Connection con = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSnow44393(String queryResultFormat) throws Exception { + try (Connection con = getConn(queryResultFormat)) { assertFalse( con.createStatement() .execute("alter session set timestamp_ntz_output_format='YYYY-MM-DD HH24:MI:SS'")); @@ -697,10 +717,11 @@ public void testSnow44393() throws Exception { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testAddBatchNumericNullFloatMixed() throws Exception { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testAddBatchNumericNullFloatMixed(String queryResultFormat) throws Exception { + try (Connection connection = getConn(queryResultFormat)) { for (int threshold = 0; threshold < 2; ++threshold) { connection .createStatement() @@ -776,9 +797,10 @@ public void testAddBatchNumericNullFloatMixed() throws Exception { } } - @Test - public void testInvalidUsageOfApi() throws Exception { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testInvalidUsageOfApi(String queryResultFormat) throws Exception { + try (Connection connection = getConn(queryResultFormat); PreparedStatement preparedStatement = connection.prepareStatement("select 1")) { final int expectedCode = ErrorCode.UNSUPPORTED_STATEMENT_TYPE_IN_EXECUTION_API.getMessageCode(); @@ -815,7 +837,7 @@ public void run() throws SQLException { private void assertException(RunnableWithSQLException runnable, int expectedCode) { try { runnable.run(); - Assert.fail(); + fail(); } catch (SQLException e) { assertThat(e.getErrorCode(), is(expectedCode)); } @@ -825,9 +847,10 @@ private interface RunnableWithSQLException { void run() throws SQLException; } - @Test - public void testCreatePreparedStatementWithParameters() throws Throwable { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testCreatePreparedStatementWithParameters(String queryResultFormat) throws Throwable { + try (Connection connection = getConn(queryResultFormat)) { connection.prepareStatement( "select 1", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); try { @@ -855,9 +878,10 @@ public void testCreatePreparedStatementWithParameters() throws Throwable { } } - @Test - public void testPrepareAndGetMeta() throws SQLException { - try (Connection con = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareAndGetMeta(String queryResultFormat) throws SQLException { + try (Connection con = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = con.prepareStatement("select 1 where 1 > ?")) { ResultSetMetaData meta = prepStatement.getMetaData(); assertThat(meta.getColumnCount(), is(1)); diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatement2LatestIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatement2LatestIT.java index f7ca395de..563406d23 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatement2LatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedStatement2LatestIT.java @@ -6,22 +6,23 @@ import static net.snowflake.client.jdbc.PreparedStatement1IT.bindOneParamSet; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Assert; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** * PreparedStatement integration tests for the latest JDBC driver. This doesn't work for the oldest @@ -29,19 +30,13 @@ * if the tests still are not applicable. If it is applicable, move tests to PreparedStatement2IT so * that both the latest and oldest supported driver run the tests. */ -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class PreparedStatement2LatestIT extends PreparedStatement0IT { - public PreparedStatement2LatestIT() { - super("json"); - } - - PreparedStatement2LatestIT(String queryFormat) { - super(queryFormat); - } - @Test - public void testPrepareUDTF() throws Exception { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testPrepareUDTF(String queryResultFormat) throws Exception { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("create or replace table employee(id number, address text)"); @@ -77,10 +72,10 @@ public void testPrepareUDTF() throws Exception { // second argument is invalid prepStatement.setInt(1, 1); prepStatement.execute(); - Assert.fail(); + fail(); } catch (SQLException e) { // failed because argument type did not match - Assert.assertThat(e.getErrorCode(), is(1044)); + assertThat(e.getErrorCode(), is(1044)); } // create a udf with same name but different arguments and return type @@ -110,9 +105,10 @@ public void testPrepareUDTF() throws Exception { * SNOW-88426: skip bind parameter index check if prepare fails and defer the error checks to * execute */ - @Test - public void testSelectWithBinding() throws Throwable { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSelectWithBinding(String queryResultFormat) throws Throwable { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("create or replace table TESTNULL(created_time timestamp_ntz, mid int)"); @@ -144,9 +140,10 @@ public void testSelectWithBinding() throws Throwable { } } - @Test - public void testLimitBind() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testLimitBind(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { String stmtStr = "select seq4() from table(generator(rowcount=>100)) limit ?"; try (PreparedStatement prepStatement = connection.prepareStatement(stmtStr)) { prepStatement.setInt(1, 10); @@ -156,9 +153,10 @@ public void testLimitBind() throws SQLException { } /** SNOW-31746 */ - @Test - public void testConstOptLimitBind() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testConstOptLimitBind(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { String stmtStr = "select 1 limit ? offset ?"; try (PreparedStatement prepStatement = connection.prepareStatement(stmtStr)) { prepStatement.setInt(1, 10); @@ -172,10 +170,11 @@ public void testConstOptLimitBind() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testTableFuncBindInput() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testTableFuncBindInput(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement(tableFuncSQL)) { prepStatement.setInt(1, 2); try (ResultSet resultSet = prepStatement.executeQuery()) { @@ -185,9 +184,10 @@ public void testTableFuncBindInput() throws SQLException { } } - @Test - public void testExecuteLargeBatch() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testExecuteLargeBatch(String queryResultFormat) throws SQLException { + try (Connection con = getConn(queryResultFormat); Statement statement = con.createStatement()) { try { statement.execute("create or replace table mytab(id int)"); @@ -212,11 +212,12 @@ public void testExecuteLargeBatch() throws SQLException { } } - @Test - public void testRemoveExtraDescribeCalls() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testRemoveExtraDescribeCalls(String queryResultFormat) throws SQLException { String queryId1 = null; String queryId2 = null; - try (Connection connection = init(); + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("create or replace table test_uuid_with_bind(c1 number)"); @@ -264,10 +265,12 @@ public void testRemoveExtraDescribeCalls() throws SQLException { } } - @Test - public void testRemoveExtraDescribeCallsSanityCheck() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testRemoveExtraDescribeCallsSanityCheck(String queryResultFormat) + throws SQLException { String queryId1; - try (Connection connection = init()) { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement preparedStatement = connection.prepareStatement( "create or replace table test_uuid_with_bind(c1 number, c2 string)")) { @@ -307,9 +310,10 @@ public void testRemoveExtraDescribeCallsSanityCheck() throws SQLException { } } - @Test - public void testAlreadyDescribedMultipleResults() throws SQLException { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testAlreadyDescribedMultipleResults(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat)) { try (PreparedStatement prepStatement = connection.prepareStatement(insertSQL)) { bindOneParamSet(prepStatement, 1, 1.22222, (float) 1.2, "test", 12121212121L, (short) 12); prepStatement.execute(); @@ -342,9 +346,10 @@ public void testAlreadyDescribedMultipleResults() throws SQLException { * * @throws Exception */ - @Test - public void testConsecutiveBatchInsertError() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testConsecutiveBatchInsertError(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("create or replace table testStageArrayBind(c1 integer, c2 string)"); @@ -381,9 +386,10 @@ public void testConsecutiveBatchInsertError() throws SQLException { } } - @Test - public void testToString() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testToString(String queryResultFormat) throws SQLException { + try (Connection connection = getConn(queryResultFormat); PreparedStatement prepStatement = connection.prepareStatement("select current_version() --testing toString()")) { diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow1IT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow1IT.java deleted file mode 100644 index 379a471dd..000000000 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow1IT.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2012-2019 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client.jdbc; - -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; - -/** Test PreparedStatement in ARROW format 2/2 */ -@Category(TestCategoryArrow.class) -public class PreparedStatementArrow1IT extends PreparedStatement1IT { - public PreparedStatementArrow1IT() { - super("arrow"); - } -} diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow1LatestIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow1LatestIT.java deleted file mode 100644 index 5c68c198b..000000000 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow1LatestIT.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2012-2020 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client.jdbc; - -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.experimental.categories.Category; - -/** - * PreparedStatement integration tests for the latest JDBC driver. This doesn't work for the oldest - * supported driver. Drop this file when PrepareStatement1IT is dropped. - */ -@Category(TestCategoryStatement.class) -public class PreparedStatementArrow1LatestIT extends PreparedStatement1LatestIT { - public PreparedStatementArrow1LatestIT() { - super("arrow"); - } -} diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow2IT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow2IT.java deleted file mode 100644 index d2b7b9f85..000000000 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow2IT.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2012-2019 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client.jdbc; - -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; - -/** Test PreparedStatement in ARROW format 2/2 */ -@Category(TestCategoryArrow.class) -public class PreparedStatementArrow2IT extends PreparedStatement2IT { - public PreparedStatementArrow2IT() { - super("arrow"); - } -} diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow2LatestIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow2LatestIT.java deleted file mode 100644 index 9c3922de4..000000000 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatementArrow2LatestIT.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2012-2020 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client.jdbc; - -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; - -/** - * PreparedStatement integration tests for the latest JDBC driver. This doesn't work for the oldest - * supported driver. Drop this file when PrepareStatement2IT is dropped. - */ -@Category(TestCategoryArrow.class) -public class PreparedStatementArrow2LatestIT extends PreparedStatement2LatestIT { - public PreparedStatementArrow2LatestIT() { - super("arrow"); - } -} diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatementFeatureNotSupportedIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatementFeatureNotSupportedIT.java index f80a00528..be20395a5 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatementFeatureNotSupportedIT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedStatementFeatureNotSupportedIT.java @@ -6,11 +6,11 @@ import java.net.URL; import java.sql.Connection; import java.sql.PreparedStatement; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class PreparedStatementFeatureNotSupportedIT extends BaseJDBCTest { @Test public void testFeatureNotSupportedException() throws Throwable { diff --git a/src/test/java/net/snowflake/client/jdbc/PreparedStatementLargeUpdateLatestIT.java b/src/test/java/net/snowflake/client/jdbc/PreparedStatementLargeUpdateLatestIT.java index 883fe0c4d..c12242af8 100644 --- a/src/test/java/net/snowflake/client/jdbc/PreparedStatementLargeUpdateLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/PreparedStatementLargeUpdateLatestIT.java @@ -3,7 +3,7 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.spy; import java.sql.Connection; @@ -11,15 +11,14 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Map; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryStatement; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ExecTimeTelemetryData; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class PreparedStatementLargeUpdateLatestIT extends BaseJDBCTest { /** @@ -28,7 +27,7 @@ public class PreparedStatementLargeUpdateLatestIT extends BaseJDBCTest { * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testLargeUpdate() throws Throwable { try (Connection con = getConnection(); Statement statement = con.createStatement()) { @@ -64,7 +63,7 @@ public void testLargeUpdate() throws Throwable { * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testExecuteLargeBatchOverIntMax() throws SQLException { try (Connection connection = getConnection(); Statement statement = connection.createStatement()) { diff --git a/src/test/java/net/snowflake/client/jdbc/ProxyLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ProxyLatestIT.java index 4fca52d1c..85f1e1a28 100644 --- a/src/test/java/net/snowflake/client/jdbc/ProxyLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ProxyLatestIT.java @@ -1,7 +1,7 @@ package net.snowflake.client.jdbc; -import static junit.framework.TestCase.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -13,20 +13,20 @@ import java.sql.Statement; import java.util.Objects; import java.util.Properties; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; -import org.junit.After; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class ProxyLatestIT extends BaseWiremockTest { - @After + @AfterEach public void tearDown() { super.tearDown(); unsetJvmProperties(); @@ -122,9 +122,9 @@ private void verifyRequestToProxy(String pathPattern, int expectedCount) { ObjectMapper mapper = new ObjectMapper(); JsonNode json = mapper.readTree(responseString); assertEquals( - "expected request count not matched for pattern: " + pathPattern, expectedCount, - json.get("count").asInt()); + json.get("count").asInt(), + "expected request count not matched for pattern: " + pathPattern); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/test/java/net/snowflake/client/jdbc/PutFileWithSpaceIncludedIT.java b/src/test/java/net/snowflake/client/jdbc/PutFileWithSpaceIncludedIT.java index 5cd03355c..940ab44e2 100644 --- a/src/test/java/net/snowflake/client/jdbc/PutFileWithSpaceIncludedIT.java +++ b/src/test/java/net/snowflake/client/jdbc/PutFileWithSpaceIncludedIT.java @@ -3,8 +3,8 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.File; import java.io.FileInputStream; @@ -13,23 +13,22 @@ import java.sql.ResultSet; import java.sql.Statement; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.io.IOUtils; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class PutFileWithSpaceIncludedIT extends BaseJDBCTest { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; /** Test PUT command to send a data file, which file name contains a space. */ @Test - @Ignore + @Disabled public void putFileWithSpaceIncluded() throws Exception { String AWS_SECRET_KEY = TestUtil.systemGetEnv("AWS_SECRET_ACCESS_KEY"); String AWS_KEY_ID = TestUtil.systemGetEnv("AWS_ACCESS_KEY_ID"); @@ -43,7 +42,8 @@ public void putFileWithSpaceIncluded() throws Exception { assertNotNull(AWS_SECRET_KEY); assertNotNull(AWS_KEY_ID); - File dataFolder = tmpFolder.newFolder(); + File dataFolder = new File(tmpFolder, "data"); + dataFolder.mkdirs(); String tarFile = getFullPathFileInResource("snow-13400.tar"); FileInputStream fis = new FileInputStream(tarFile); TarArchiveInputStream tis = new TarArchiveInputStream(fis); diff --git a/src/test/java/net/snowflake/client/jdbc/PutUnescapeBackslashIT.java b/src/test/java/net/snowflake/client/jdbc/PutUnescapeBackslashIT.java index f9579636d..5de4ec5bf 100644 --- a/src/test/java/net/snowflake/client/jdbc/PutUnescapeBackslashIT.java +++ b/src/test/java/net/snowflake/client/jdbc/PutUnescapeBackslashIT.java @@ -18,15 +18,15 @@ import java.sql.ResultSet; import java.sql.Statement; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import org.apache.commons.io.FileUtils; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class PutUnescapeBackslashIT extends AbstractDriverIT { - @BeforeClass + @BeforeAll public static void setUpClass() throws Exception {} /** diff --git a/src/test/java/net/snowflake/client/jdbc/RestRequestTest.java b/src/test/java/net/snowflake/client/jdbc/RestRequestTest.java index 6e1a26428..7fdaab9bb 100644 --- a/src/test/java/net/snowflake/client/jdbc/RestRequestTest.java +++ b/src/test/java/net/snowflake/client/jdbc/RestRequestTest.java @@ -3,14 +3,14 @@ */ package net.snowflake.client.jdbc; +import static net.snowflake.client.AssumptionUtils.assumeRunningOnLinuxMac; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import net.snowflake.client.RunningNotOnLinuxMac; import net.snowflake.client.core.ExecTimeTelemetryData; import net.snowflake.client.core.HttpUtil; import net.snowflake.client.jdbc.telemetryOOB.TelemetryService; @@ -32,7 +31,7 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.CloseableHttpClient; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -337,18 +336,16 @@ class TestCase { for (TestCase t : testCases) { if (t.result) { assertTrue( + RestRequest.isNonRetryableHTTPCode(anyStatusCodeResponse(t.statusCode), t.retryHTTP403), String.format( "Result must be true but false: HTTP Code: %d, RetryHTTP403: %s", - t.statusCode, t.retryHTTP403), - RestRequest.isNonRetryableHTTPCode( - anyStatusCodeResponse(t.statusCode), t.retryHTTP403)); + t.statusCode, t.retryHTTP403)); } else { assertFalse( + RestRequest.isNonRetryableHTTPCode(anyStatusCodeResponse(t.statusCode), t.retryHTTP403), String.format( "Result must be false but true: HTTP Code: %d, RetryHTTP403: %s", - t.statusCode, t.retryHTTP403), - RestRequest.isNonRetryableHTTPCode( - anyStatusCodeResponse(t.statusCode), t.retryHTTP403)); + t.statusCode, t.retryHTTP403)); } } } @@ -459,8 +456,8 @@ public CloseableHttpResponse answer(InvocationOnMock invocation) throws Throwabl execute(client, "fakeurl.com/?requestId=abcd-1234", 0, 0, 0, true, false); } - @Test(expected = SnowflakeSQLException.class) - public void testMaxRetriesExceeded() throws IOException, SnowflakeSQLException { + @Test + public void testMaxRetriesExceeded() throws IOException { boolean telemetryEnabled = TelemetryService.getInstance().isEnabled(); CloseableHttpClient client = mock(CloseableHttpClient.class); @@ -482,8 +479,9 @@ public CloseableHttpResponse answer(InvocationOnMock invocation) throws Throwabl try { TelemetryService.disable(); - execute(client, "fakeurl.com/?requestId=abcd-1234", 0, 0, 0, true, false, 1); - fail("testMaxRetries"); + assertThrows( + SnowflakeSQLException.class, + () -> execute(client, "fakeurl.com/?requestId=abcd-1234", 0, 0, 0, true, false, 1)); } finally { if (telemetryEnabled) { TelemetryService.enable(); @@ -516,8 +514,8 @@ public CloseableHttpResponse answer(InvocationOnMock invocationOnMock) execute(client, "fakeurl.com/?requestId=abcd-1234", 0, 0, 0, true, false, 1); } - @Test(expected = SnowflakeSQLException.class) - public void testLoginMaxRetries() throws IOException, SnowflakeSQLException { + @Test + public void testLoginMaxRetries() throws IOException { boolean telemetryEnabled = TelemetryService.getInstance().isEnabled(); CloseableHttpClient client = mock(CloseableHttpClient.class); @@ -539,8 +537,9 @@ public CloseableHttpResponse answer(InvocationOnMock invocation) throws Throwabl try { TelemetryService.disable(); - execute(client, "/session/v1/login-request", 0, 0, 0, true, false, 1); - fail("testMaxRetries"); + assertThrows( + SnowflakeSQLException.class, + () -> execute(client, "/session/v1/login-request", 0, 0, 0, true, false, 1)); } finally { if (telemetryEnabled) { TelemetryService.enable(); @@ -552,7 +551,7 @@ public CloseableHttpResponse answer(InvocationOnMock invocation) throws Throwabl @Test public void testLoginTimeout() throws IOException { - assumeFalse(RunningNotOnLinuxMac.isNotRunningOnLinuxMac()); + assumeRunningOnLinuxMac(); boolean telemetryEnabled = TelemetryService.getInstance().isEnabled(); CloseableHttpClient client = mock(CloseableHttpClient.class); @@ -643,18 +642,18 @@ public void shouldGenerateBackoffInRangeExceptTheLastBackoff() { elapsedMilliForTransientIssues); assertTrue( - "Backoff should be lower or equal to max backoff limit", - backoffInMilli <= maxBackoffInMilli); + backoffInMilli <= maxBackoffInMilli, + "Backoff should be lower or equal to max backoff limit"); if (elapsedMilliForTransientIssues + backoffInMilli >= retryTimeoutInMilli) { assertEquals( - "Backoff should fill time till retry timeout", retryTimeoutInMilli - elapsedMilliForTransientIssues, - backoffInMilli); + backoffInMilli, + "Backoff should fill time till retry timeout"); break; } else { assertTrue( - "Backoff should be higher or equal to min backoff limit", - backoffInMilli >= minBackoffInMilli); + backoffInMilli >= minBackoffInMilli, + "Backoff should be higher or equal to min backoff limit"); } elapsedMilliForTransientIssues += backoffInMilli; } diff --git a/src/test/java/net/snowflake/client/jdbc/RestRequestWiremockLatestIT.java b/src/test/java/net/snowflake/client/jdbc/RestRequestWiremockLatestIT.java index 76856b985..505af0b53 100644 --- a/src/test/java/net/snowflake/client/jdbc/RestRequestWiremockLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/RestRequestWiremockLatestIT.java @@ -1,16 +1,16 @@ package net.snowflake.client.jdbc; import java.util.concurrent.atomic.AtomicBoolean; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ExecTimeTelemetryData; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class RestRequestWiremockLatestIT extends BaseWiremockTest { String connectionResetByPeerScenario = diff --git a/src/test/java/net/snowflake/client/jdbc/ResultJsonParserV2Test.java b/src/test/java/net/snowflake/client/jdbc/ResultJsonParserV2Test.java index 7349a26f2..0675b9758 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultJsonParserV2Test.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultJsonParserV2Test.java @@ -3,14 +3,14 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import net.snowflake.client.core.SFSession; import org.apache.commons.text.StringEscapeUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** This is the unit tests for ResultJsonParserV2 */ public class ResultJsonParserV2Test { diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSet0IT.java b/src/test/java/net/snowflake/client/jdbc/ResultSet0IT.java index 90cc98aa6..bebe0a54e 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSet0IT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSet0IT.java @@ -3,24 +3,22 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Before; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; /** Result set test base class. */ -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSet0IT extends BaseJDBCWithSharedConnectionIT { - private final String queryResultFormat; - - public Connection init(Properties paramProperties) throws SQLException { + public Connection init(Properties paramProperties, String queryResultFormat) throws SQLException { Connection conn = BaseJDBCTest.getConnection(DONT_INJECT_SOCKET_TIMEOUT, paramProperties, false, false); try (Statement stmt = conn.createStatement()) { @@ -29,11 +27,9 @@ public Connection init(Properties paramProperties) throws SQLException { return conn; } - @Before + @BeforeEach public void setUp() throws SQLException { try (Statement statement = connection.createStatement()) { - - statement.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); // TEST_RS statement.execute("create or replace table test_rs (colA string)"); statement.execute("insert into test_rs values('rowOne')"); @@ -50,22 +46,22 @@ public void setUp() throws SQLException { + "error_on_column_count_mismatch=false)"); // put files assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%orders_jdbc")); + "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%orders_jdbc"), + "Failed to put a file"); assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE_2) + " @%orders_jdbc")); + "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE_2) + " @%orders_jdbc"), + "Failed to put a file"); int numRows = statement.executeUpdate("copy into orders_jdbc"); - assertEquals("Unexpected number of rows copied: " + numRows, 73, numRows); + assertEquals(73, numRows, "Unexpected number of rows copied: " + numRows); } } - ResultSet numberCrossTesting() throws SQLException { - Statement statement = connection.createStatement(); + ResultSet numberCrossTesting(String queryResultFormat) throws SQLException { + Statement statement = createStatement(queryResultFormat); statement.execute( "create or replace table test_types(c1 number, c2 integer, c3 float, c4 boolean," + "c5 char, c6 varchar, c7 date, c8 datetime, c9 time, c10 timestamp_ltz, " @@ -80,8 +76,4 @@ ResultSet numberCrossTesting() throws SQLException { statement.execute("insert into test_types (c5, c6) values('h', 'hello')"); return statement.executeQuery("select * from test_types"); } - - ResultSet0IT(String queryResultFormat) { - this.queryResultFormat = queryResultFormat; - } } diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetAlreadyClosedIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetAlreadyClosedIT.java index 82f3c3244..091c7928e 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetAlreadyClosedIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetAlreadyClosedIT.java @@ -3,8 +3,8 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.sql.DatabaseMetaData; @@ -12,11 +12,11 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Calendar; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSetAlreadyClosedIT extends BaseJDBCWithSharedConnectionIT { @Test diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForce0MultiTimeZone.java b/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForce0MultiTimeZone.java index c6edc67fb..69c6031f3 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForce0MultiTimeZone.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForce0MultiTimeZone.java @@ -6,46 +6,52 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; import java.util.List; import java.util.TimeZone; -import org.junit.After; -import org.junit.Before; +import net.snowflake.client.providers.ProvidersUtil; +import net.snowflake.client.providers.ScaleProvider; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import net.snowflake.client.providers.SnowflakeArgumentsProvider; +import net.snowflake.client.providers.TimezoneProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; abstract class ResultSetArrowForce0MultiTimeZone extends BaseJDBCTest { - static List testData() { - String[] timeZones = new String[] {"UTC", "America/New_York", "MEZ"}; - String[] queryFormats = new String[] {"json", "arrow"}; - List ret = new ArrayList<>(); - for (String queryFormat : queryFormats) { - for (String timeZone : timeZones) { - ret.add(new Object[] {queryFormat, timeZone}); - } + protected static class DataProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return ProvidersUtil.cartesianProduct( + context, new SimpleResultFormatProvider(), new TimezoneProvider(3)); } - return ret; } - protected final String queryResultFormat; - protected final String tz; - private TimeZone origTz; - - ResultSetArrowForce0MultiTimeZone(String queryResultFormat, String timeZone) { - this.queryResultFormat = queryResultFormat; - this.tz = timeZone; + protected static class DataWithScaleProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return ProvidersUtil.cartesianProduct(context, new DataProvider(), new ScaleProvider()); + } } - @Before - public void setUp() { + private static TimeZone origTz; + + @BeforeAll + public static void setUp() { origTz = TimeZone.getDefault(); - TimeZone.setDefault(TimeZone.getTimeZone(this.tz)); } - @After - public void tearDown() { + @AfterAll + public static void tearDown() { TimeZone.setDefault(origTz); } - Connection init(String table, String column, String values) throws SQLException { + protected static void setTimezone(String tz) { + TimeZone.setDefault(TimeZone.getTimeZone(tz)); + } + + Connection init(String table, String column, String values, String queryResultFormat) + throws SQLException { Connection con = BaseJDBCTest.getConnection(); try (Statement statement = con.createStatement()) { diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForceLTZMultiTimeZoneIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForceLTZMultiTimeZoneIT.java index f998fb5d4..a612870a5 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForceLTZMultiTimeZoneIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForceLTZMultiTimeZoneIT.java @@ -3,45 +3,31 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.text.SimpleDateFormat; -import java.util.Collection; import java.util.TimeZone; -import net.snowflake.client.category.TestCategoryArrow; +import net.snowflake.client.category.TestTags; import org.apache.commons.lang3.StringUtils; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** Compare json and arrow resultSet behaviors 1/2 */ -@RunWith(Parameterized.class) -@Category(TestCategoryArrow.class) +@Tag(TestTags.ARROW) public class ResultSetArrowForceLTZMultiTimeZoneIT extends ResultSetArrowForce0MultiTimeZone { - @Parameterized.Parameters(name = "format={0}, tz={1}") - public static Collection data() { - return ResultSetArrowForce0MultiTimeZone.testData(); - } - - public ResultSetArrowForceLTZMultiTimeZoneIT(String queryResultFormat, String timeZone) { - super(queryResultFormat, timeZone); - } - - @Test - public void testTimestampLTZ() throws SQLException { - for (int scale = 0; scale <= 9; scale++) { - testTimestampLTZWithScale(scale); - } - } - private void testTimestampLTZWithScale(int scale) throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataWithScaleProvider.class) + public void testTimestampLTZWithScale(String queryResultFormat, String tz, int scale) + throws SQLException { + setTimezone(tz); String[] cases = { "2017-01-01 12:00:00 Z", "2014-01-02 16:00:00 Z", @@ -72,7 +58,7 @@ private void testTimestampLTZWithScale(int scale) throws SQLException { String column = "(a timestamp_ltz(" + scale + "))"; String values = "('" + StringUtils.join(cases, "'),('") + "'), (null)"; - Connection con = init(table, column, values); + Connection con = init(table, column, values, queryResultFormat); ResultSet rs = con.createStatement().executeQuery("select * from " + table); int i = 0; while (i < cases.length) { @@ -85,8 +71,11 @@ private void testTimestampLTZWithScale(int scale) throws SQLException { finish(table, con); } - @Test - public void testTimestampLTZOutputFormat() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampLTZOutputFormat(String queryResultFormat, String tz) + throws SQLException { + setTimezone(tz); String[] cases = {"2017-01-01 12:00:00 Z", "2014-01-02 16:00:00 Z", "2014-01-02 12:34:56 Z"}; long[] times = {1483272000000L, 1388678400000L, 1388666096000L}; @@ -99,7 +88,7 @@ public void testTimestampLTZOutputFormat() throws SQLException { String column = "(a timestamp_ltz)"; String values = "('" + StringUtils.join(cases, "'),('") + "')"; - try (Connection con = init(table, column, values); + try (Connection con = init(table, column, values, queryResultFormat); Statement statement = con.createStatement()) { try { // use initialized ltz output format @@ -146,13 +135,14 @@ public void testTimestampLTZOutputFormat() throws SQLException { } } finally { statement.execute("drop table " + table); - System.clearProperty("user.timezone"); } } } - @Test - public void testTimestampLTZWithNulls() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampLTZWithNulls(String queryResultFormat, String tz) throws SQLException { + setTimezone(tz); String[] cases = { "2017-01-01 12:00:00 Z", "2014-01-02 16:00:00 Z", @@ -183,7 +173,7 @@ public void testTimestampLTZWithNulls() throws SQLException { String column = "(a timestamp_ltz)"; String values = "('" + StringUtils.join(cases, "'), (null),('") + "')"; - try (Connection con = init(table, column, values); + try (Connection con = init(table, column, values, queryResultFormat); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -200,13 +190,14 @@ public void testTimestampLTZWithNulls() throws SQLException { } } finally { statement.execute("drop table " + table); - System.clearProperty("user.timezone"); } } } - @Test - public void testTimestampLTZWithNanos() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampLTZWithNanos(String queryResultFormat, String tz) throws SQLException { + setTimezone(tz); String[] cases = { "2017-01-01 12:00:00.123456789", "2014-01-02 16:00:00.000000001", @@ -229,7 +220,7 @@ public void testTimestampLTZWithNanos() throws SQLException { String column = "(a timestamp_ltz)"; String values = "('" + StringUtils.join(cases, " Z'),('") + " Z'), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(table, column, values, queryResultFormat); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -243,7 +234,6 @@ public void testTimestampLTZWithNanos() throws SQLException { assertNull(rs.getString(1)); } finally { statement.execute("drop table " + table); - System.clearProperty("user.timezone"); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForceTZMultiTimeZoneIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForceTZMultiTimeZoneIT.java index e073bfccf..db0984081 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForceTZMultiTimeZoneIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetArrowForceTZMultiTimeZoneIT.java @@ -3,43 +3,29 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.Collection; -import net.snowflake.client.category.TestCategoryArrow; +import net.snowflake.client.category.TestTags; import org.apache.commons.lang3.StringUtils; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** Compare json and arrow resultSet behaviors 2/2 */ -@RunWith(Parameterized.class) -@Category(TestCategoryArrow.class) +@Tag(TestTags.ARROW) public class ResultSetArrowForceTZMultiTimeZoneIT extends ResultSetArrowForce0MultiTimeZone { - @Parameterized.Parameters(name = "format={0}, tz={1}") - public static Collection data() { - return ResultSetArrowForce0MultiTimeZone.testData(); - } - - public ResultSetArrowForceTZMultiTimeZoneIT(String queryResultFormat, String timeZone) { - super(queryResultFormat, timeZone); - } - - @Test - public void testTimestampTZ() throws SQLException { - for (int scale = 0; scale <= 9; scale++) { - testTimestampTZWithScale(scale); - } - } - private void testTimestampTZWithScale(int scale) throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataWithScaleProvider.class) + public void testTimestampTZWithScale(String queryResultFormat, String tz, int scale) + throws SQLException { + setTimezone(tz); String[] cases = { "2017-01-01 12:00:00 Z", "2014-01-02 16:00:00 Z", @@ -67,7 +53,7 @@ private void testTimestampTZWithScale(int scale) throws SQLException { String column = "(a timestamp_tz(" + scale + "))"; String values = "('" + StringUtils.join(cases, "'),('") + "'), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(table, column, values, queryResultFormat); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -81,13 +67,14 @@ private void testTimestampTZWithScale(int scale) throws SQLException { assertNull(rs.getString(1)); } finally { statement.execute("drop table " + table); - System.clearProperty("user.timezone"); } } } - @Test - public void testTimestampTZWithNanos() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampTZWithNanos(String queryResultFormat, String tz) throws SQLException { + setTimezone(tz); String[] cases = { "2017-01-01 12:00:00.1", "2014-01-02 16:00:00.123456789", @@ -119,7 +106,7 @@ public void testTimestampTZWithNanos() throws SQLException { String column = "(a timestamp_tz)"; String values = "('" + StringUtils.join(cases, " Z'),('") + " Z'), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(table, column, values, queryResultFormat); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -138,13 +125,14 @@ public void testTimestampTZWithNanos() throws SQLException { assertNull(rs.getString(1)); } finally { statement.execute("drop table " + table); - System.clearProperty("user.timezone"); } } } - @Test - public void testTimestampTZWithMicros() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampTZWithMicros(String queryResultFormat, String tz) throws SQLException { + setTimezone(tz); String[] cases = { "2017-01-01 12:00:00.1", "2014-01-02 16:00:00.123456", @@ -178,7 +166,7 @@ public void testTimestampTZWithMicros() throws SQLException { String column = "(a timestamp_tz(6))"; String values = "('" + StringUtils.join(cases, " Z'),('") + " Z'), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(table, column, values, queryResultFormat); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -197,7 +185,6 @@ public void testTimestampTZWithMicros() throws SQLException { assertNull(rs.getString(1)); } finally { statement.execute("drop table " + table); - System.clearProperty("user.timezone"); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetArrowIT.java deleted file mode 100644 index a7e982024..000000000 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowIT.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2012-2020 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client.jdbc; - -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; - -@Category(TestCategoryArrow.class) -public class ResultSetArrowIT extends ResultSetIT { - public ResultSetArrowIT() { - super("arrow"); - } -} diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetArrowLatestIT.java deleted file mode 100644 index 4ea7f7d8f..000000000 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetArrowLatestIT.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2012-2020 Snowflake Computing Inc. All right reserved. - */ -package net.snowflake.client.jdbc; - -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; - -/** - * ResultSet integration tests for the latest JDBC driver. This doesn't work for the oldest - * supported driver. Drop this file when ResultSetLatestIT is dropped. - */ -@Category(TestCategoryArrow.class) -public class ResultSetArrowLatestIT extends ResultSetLatestIT { - public ResultSetArrowLatestIT() { - super("arrow"); - } -} diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetAsyncIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetAsyncIT.java index a6a63a65d..b86a65c95 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetAsyncIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetAsyncIT.java @@ -4,11 +4,11 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.Reader; import java.math.BigDecimal; @@ -28,13 +28,13 @@ import java.util.List; import java.util.Map; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryResultSet; +import net.snowflake.client.category.TestTags; import net.snowflake.common.core.SqlState; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Test AsyncResultSet */ -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSetAsyncIT extends BaseJDBCWithSharedConnectionIT { @Test @@ -155,11 +155,11 @@ public void testOrderAndClosureFunctions() throws SQLException { statement.unwrap(SnowflakeStatement.class).executeAsyncQuery("select * from test_rsmd"); // test isFirst, isBeforeFirst - assertTrue("should be before the first", resultSet.isBeforeFirst()); - assertFalse("should not be the first", resultSet.isFirst()); + assertTrue(resultSet.isBeforeFirst(), "should be before the first"); + assertFalse(resultSet.isFirst(), "should not be the first"); resultSet.next(); - assertFalse("should not be before the first", resultSet.isBeforeFirst()); - assertTrue("should be the first", resultSet.isFirst()); + assertFalse(resultSet.isBeforeFirst(), "should not be before the first"); + assertTrue(resultSet.isFirst(), "should be the first"); // test isClosed functions queryID = resultSet.unwrap(SnowflakeResultSet.class).getQueryID(); diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetAsyncLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetAsyncLatestIT.java index dd534d469..9bcbd83b4 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetAsyncLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetAsyncLatestIT.java @@ -4,19 +4,19 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Test AsyncResultSet */ -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSetAsyncLatestIT extends BaseJDBCTest { @Test public void testAsyncResultSet() throws SQLException { diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetFeatureNotSupportedIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetFeatureNotSupportedIT.java index 423661c77..8f9da34e1 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetFeatureNotSupportedIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetFeatureNotSupportedIT.java @@ -9,11 +9,11 @@ import java.sql.Time; import java.sql.Timestamp; import java.util.Collections; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSetFeatureNotSupportedIT extends BaseJDBCWithSharedConnectionIT { @Test public void testQueryResultSetNotSupportedException() throws Throwable { diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetIT.java index 193246368..760d83a75 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetIT.java @@ -7,13 +7,13 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.InputStream; import java.io.InputStreamReader; @@ -30,14 +30,15 @@ import java.sql.Statement; import java.sql.Types; import java.util.Properties; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** Test ResultSet */ -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSetIT extends ResultSet0IT { private final String selectAllSQL = "select * from test_rs"; @@ -47,25 +48,19 @@ public class ResultSetIT extends ResultSet0IT { (byte) 0x00, (byte) 0xFF, (byte) 0x42, (byte) 0x01 }; - public ResultSetIT() { - this("json"); - } - - ResultSetIT(String queryResultFormat) { - super(queryResultFormat); - } - - @Test - public void testFindColumn() throws SQLException { - try (Statement statement = connection.createStatement(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testFindColumn(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat); ResultSet resultSet = statement.executeQuery(selectAllSQL)) { assertEquals(1, resultSet.findColumn("COLA")); } } - @Test - public void testGetColumnClassNameForBinary() throws Throwable { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetColumnClassNameForBinary(String queryResultFormat) throws Throwable { + try (Statement statement = createStatement(queryResultFormat); ) { try { statement.execute("create or replace table bintable (b binary)"); statement.execute("insert into bintable values ('00f1f2')"); @@ -88,8 +83,9 @@ public void testGetColumnClassNameForBinary() throws Throwable { } } - @Test - public void testGetMethod() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetMethod(String queryResultFormat) throws Throwable { String prepInsertString = "insert into test_get values(?, ?, ?, ?, ?, ?, ?, ?)"; int bigInt = Integer.MAX_VALUE; long bigLong = Long.MAX_VALUE; @@ -100,7 +96,7 @@ public void testGetMethod() throws Throwable { Clob clob = connection.createClob(); clob.setString(1, "hello world"); - try (Statement statement = connection.createStatement()) { + try (Statement statement = createStatement(queryResultFormat)) { try { statement.execute( "create or replace table test_get(colA integer, colB number, colC number, " @@ -153,8 +149,11 @@ public void testGetMethod() throws Throwable { } } - @Test - public void testGetObjectOnDatabaseMetadataResultSet() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetObjectOnDatabaseMetadataResultSet(String queryResultFormat) + throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) {} DatabaseMetaData databaseMetaData = connection.getMetaData(); try (ResultSet resultSet = databaseMetaData.getTypeInfo()) { assertTrue(resultSet.next()); @@ -163,9 +162,10 @@ public void testGetObjectOnDatabaseMetadataResultSet() throws SQLException { } } - @Test - public void testGetShort() throws SQLException { - try (ResultSet resultSet = numberCrossTesting()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetShort(String queryResultFormat) throws SQLException { + try (ResultSet resultSet = numberCrossTesting(queryResultFormat)) { assertTrue(resultSet.next()); // assert that 0 is returned for null values for every type of value for (int i = 1; i < 13; i++) { @@ -205,9 +205,10 @@ public void testGetShort() throws SQLException { } } - @Test - public void testGetInt() throws SQLException { - try (ResultSet resultSet = numberCrossTesting()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetInt(String queryResultFormat) throws SQLException { + try (ResultSet resultSet = numberCrossTesting(queryResultFormat)) { assertTrue(resultSet.next()); // assert that 0 is returned for null values for every type of value for (int i = 1; i < 13; i++) { @@ -246,9 +247,10 @@ public void testGetInt() throws SQLException { } } - @Test - public void testGetLong() throws SQLException { - try (ResultSet resultSet = numberCrossTesting()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetLong(String queryResultFormat) throws SQLException { + try (ResultSet resultSet = numberCrossTesting(queryResultFormat)) { assertTrue(resultSet.next()); // assert that 0 is returned for null values for every type of value for (int i = 1; i < 13; i++) { @@ -287,9 +289,10 @@ public void testGetLong() throws SQLException { } } - @Test - public void testGetFloat() throws SQLException { - try (ResultSet resultSet = numberCrossTesting()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetFloat(String queryResultFormat) throws SQLException { + try (ResultSet resultSet = numberCrossTesting(queryResultFormat)) { assertTrue(resultSet.next()); // assert that 0 is returned for null values for every type of value for (int i = 1; i < 13; i++) { @@ -328,9 +331,10 @@ public void testGetFloat() throws SQLException { } } - @Test - public void testGetDouble() throws SQLException { - try (ResultSet resultSet = numberCrossTesting()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetDouble(String queryResultFormat) throws SQLException { + try (ResultSet resultSet = numberCrossTesting(queryResultFormat)) { assertTrue(resultSet.next()); // assert that 0 is returned for null values for every type of value for (int i = 1; i < 13; i++) { @@ -369,9 +373,10 @@ public void testGetDouble() throws SQLException { } } - @Test - public void testGetBigDecimal() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetBigDecimal(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute("create or replace table test_get(colA number(38,9))"); try (PreparedStatement preparedStatement = connection.prepareStatement("insert into test_get values(?)")) { @@ -393,7 +398,7 @@ public void testGetBigDecimal() throws SQLException { statement.execute("drop table if exists test_get"); } - try (ResultSet resultSet = numberCrossTesting()) { + try (ResultSet resultSet = numberCrossTesting(queryResultFormat)) { assertTrue(resultSet.next()); for (int i = 1; i < 13; i++) { assertNull(resultSet.getBigDecimal(i)); @@ -426,9 +431,10 @@ public void testGetBigDecimal() throws SQLException { } } - @Test - public void testGetBigDecimalNegative() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetBigDecimalNegative(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { try { statement.execute("create or replace table test_dec(colA time)"); try (PreparedStatement preparedStatement = @@ -454,9 +460,10 @@ public void testGetBigDecimalNegative() throws SQLException { } } - @Test - public void testCursorPosition() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testCursorPosition(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute(selectAllSQL); try (ResultSet resultSet = statement.getResultSet()) { assertTrue(resultSet.next()); @@ -480,10 +487,11 @@ public void testCursorPosition() throws SQLException { * * @throws SQLException arises if any exception occurs. */ - @Test - public void testGetBytes() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetBytes(String queryResultFormat) throws SQLException { Properties props = new Properties(); - try (Connection connection = init(props); + try (Connection connection = init(props, queryResultFormat); Statement statement = connection.createStatement()) { try { ingestBinaryTestData(connection); @@ -530,11 +538,12 @@ private void ingestBinaryTestData(Connection connection) throws SQLException { * * @throws Exception arises if any error occurs */ - @Test - public void testGetBytesInBase64() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetBytesInBase64(String queryResultFormat) throws Exception { Properties props = new Properties(); props.setProperty("binary_output_format", "BAse64"); - try (Connection connection = init(props); + try (Connection connection = init(props, queryResultFormat); Statement statement = connection.createStatement()) { try { ingestBinaryTestData(connection); @@ -557,9 +566,10 @@ public void testGetBytesInBase64() throws Exception { } // SNOW-31647 - @Test - public void testColumnMetaWithZeroPrecision() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testColumnMetaWithZeroPrecision(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { try { statement.execute( "create or replace table testColDecimal(cola number(38, 0), " + "colb number(17, 5))"); @@ -578,9 +588,10 @@ public void testColumnMetaWithZeroPrecision() throws SQLException { } } - @Test - public void testGetObjectOnFixedView() throws Exception { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetObjectOnFixedView(String queryResultFormat) throws Exception { + try (Statement statement = createStatement(queryResultFormat)) { try { statement.execute( "create or replace table testFixedView" @@ -592,9 +603,9 @@ public void testGetObjectOnFixedView() throws Exception { // put files assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%testFixedView")); + "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%testFixedView"), + "Failed to put a file"); try (ResultSet resultSet = statement.executeQuery( @@ -613,11 +624,12 @@ public void testGetObjectOnFixedView() throws Exception { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testGetColumnDisplaySizeAndPrecision() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testGetColumnDisplaySizeAndPrecision(String queryResultFormat) throws SQLException { ResultSetMetaData resultSetMetaData = null; - try (Statement statement = connection.createStatement()) { + try (Statement statement = createStatement(queryResultFormat)) { try (ResultSet resultSet = statement.executeQuery("select cast(1 as char)")) { resultSetMetaData = resultSet.getMetaData(); @@ -665,9 +677,10 @@ public void testGetColumnDisplaySizeAndPrecision() throws SQLException { } } - @Test - public void testGetBoolean() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetBoolean(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute("create or replace table testBoolean(cola boolean)"); statement.execute("insert into testBoolean values(false)"); try (ResultSet resultSet = statement.executeQuery("select * from testBoolean")) { @@ -733,9 +746,10 @@ public void testGetBoolean() throws SQLException { } } - @Test - public void testGetClob() throws Throwable { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetClob(String queryResultFormat) throws Throwable { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute("create or replace table testClob(cola text)"); statement.execute("insert into testClob values('hello world')"); statement.execute("insert into testClob values('hello world1')"); @@ -772,9 +786,10 @@ public void testGetClob() throws Throwable { } } - @Test - public void testFetchOnClosedResultSet() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testFetchOnClosedResultSet(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { ResultSet resultSet = statement.executeQuery(selectAllSQL); assertFalse(resultSet.isClosed()); resultSet.close(); @@ -783,11 +798,13 @@ public void testFetchOnClosedResultSet() throws SQLException { } } - @Test - public void testReleaseDownloaderCurrentMemoryUsage() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testReleaseDownloaderCurrentMemoryUsage(String queryResultFormat) + throws SQLException { final long initialMemoryUsage = SnowflakeChunkDownloader.getCurrentMemoryUsage(); - try (Statement statement = connection.createStatement()) { + try (Statement statement = createStatement(queryResultFormat)) { statement.executeQuery( "select current_date(), true,2345234, 2343.0, 'testrgint\\n\\t' from table(generator(rowcount=>1000000))"); @@ -802,21 +819,25 @@ public void testReleaseDownloaderCurrentMemoryUsage() throws SQLException { equalTo(initialMemoryUsage)); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testResultColumnSearchCaseSensitiveOld() throws Exception { - subTestResultColumnSearchCaseSensitive("JDBC_RS_COLUMN_CASE_INSENSITIVE"); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testResultColumnSearchCaseSensitiveOld(String queryResultFormat) throws Exception { + subTestResultColumnSearchCaseSensitive("JDBC_RS_COLUMN_CASE_INSENSITIVE", queryResultFormat); } - @Test - public void testResultColumnSearchCaseSensitive() throws Exception { - subTestResultColumnSearchCaseSensitive("CLIENT_RESULT_COLUMN_CASE_INSENSITIVE"); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testResultColumnSearchCaseSensitive(String queryResultFormat) throws Exception { + subTestResultColumnSearchCaseSensitive( + "CLIENT_RESULT_COLUMN_CASE_INSENSITIVE", queryResultFormat); } - private void subTestResultColumnSearchCaseSensitive(String parameterName) throws Exception { + private void subTestResultColumnSearchCaseSensitive( + String parameterName, String queryResultFormat) throws Exception { Properties prop = new Properties(); prop.put("tracing", "FINEST"); - try (Connection connection = init(prop); + try (Connection connection = init(prop, queryResultFormat); Statement statement = connection.createStatement()) { try (ResultSet resultSet = statement.executeQuery("select 1 AS TESTCOL")) { @@ -847,9 +868,10 @@ private void subTestResultColumnSearchCaseSensitive(String parameterName) throws } } - @Test - public void testInvalidColumnIndex() throws SQLException { - try (Statement statement = connection.createStatement(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testInvalidColumnIndex(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat); ResultSet resultSet = statement.executeQuery(selectAllSQL)) { assertTrue(resultSet.next()); @@ -869,11 +891,11 @@ public void testInvalidColumnIndex() throws SQLException { } /** SNOW-28882: wasNull was not set properly */ - @Test - public void testWasNull() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testWasNull(String queryResultFormat) throws Exception { try (ResultSet ret = - connection - .createStatement() + createStatement(queryResultFormat) .executeQuery( "select cast(1/nullif(0,0) as double)," + "cast(1/nullif(0,0) as int), 100, " @@ -891,9 +913,10 @@ public void testWasNull() throws Exception { } /** SNOW-28390 */ - @Test - public void testParseInfAndNaNNumber() throws Exception { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testParseInfAndNaNNumber(String queryResultFormat) throws Exception { + try (Statement statement = createStatement(queryResultFormat)) { try (ResultSet ret = statement.executeQuery("select to_double('inf'), to_double('-inf')")) { assertTrue(ret.next()); assertThat("Positive Infinite Number", ret.getDouble(1), equalTo(Double.POSITIVE_INFINITY)); @@ -910,10 +933,11 @@ public void testParseInfAndNaNNumber() throws Exception { } /** SNOW-33227 */ - @Test - public void testTreatDecimalAsInt() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testTreatDecimalAsInt(String queryResultFormat) throws Exception { ResultSetMetaData metaData; - try (Statement statement = connection.createStatement()) { + try (Statement statement = createStatement(queryResultFormat)) { try (ResultSet ret = statement.executeQuery("select 1")) { metaData = ret.getMetaData(); @@ -929,60 +953,62 @@ public void testTreatDecimalAsInt() throws Exception { } } - @Test - public void testIsLast() throws Exception { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testIsLast(String queryResultFormat) throws Exception { + try (Statement statement = createStatement(queryResultFormat)) { try (ResultSet ret = statement.executeQuery("select * from orders_jdbc")) { - assertTrue("should be before the first", ret.isBeforeFirst()); - assertFalse("should not be the first", ret.isFirst()); + assertTrue(ret.isBeforeFirst(), "should be before the first"); + assertFalse(ret.isFirst(), "should not be the first"); assertTrue(ret.next()); - assertFalse("should not be before the first", ret.isBeforeFirst()); - assertTrue("should be the first", ret.isFirst()); + assertFalse(ret.isBeforeFirst(), "should not be before the first"); + assertTrue(ret.isFirst(), "should be the first"); int cnt = 0; while (ret.next()) { cnt++; if (cnt == 72) { - assertTrue("should be the last", ret.isLast()); - assertFalse("should not be after the last", ret.isAfterLast()); + assertTrue(ret.isLast(), "should be the last"); + assertFalse(ret.isAfterLast(), "should not be after the last"); } } assertEquals(72, cnt); assertFalse(ret.next()); - assertFalse("should not be the last", ret.isLast()); - assertTrue("should be afterthe last", ret.isAfterLast()); + assertFalse(ret.isLast(), "should not be the last"); + assertTrue(ret.isAfterLast(), "should be afterthe last"); } // PUT one file try (ResultSet ret = statement.executeQuery( "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @~")) { - assertTrue("should be before the first", ret.isBeforeFirst()); - assertFalse("should not be the first", ret.isFirst()); + assertTrue(ret.isBeforeFirst(), "should be before the first"); + assertFalse(ret.isFirst(), "should not be the first"); assertTrue(ret.next()); - assertFalse("should not be before the first", ret.isBeforeFirst()); - assertTrue("should be the first", ret.isFirst()); + assertFalse(ret.isBeforeFirst(), "should not be before the first"); + assertTrue(ret.isFirst(), "should be the first"); - assertTrue("should be the last", ret.isLast()); - assertFalse("should not be after the last", ret.isAfterLast()); + assertTrue(ret.isLast(), "should be the last"); + assertFalse(ret.isAfterLast(), "should not be after the last"); assertFalse(ret.next()); - assertFalse("should not be the last", ret.isLast()); - assertTrue("should be after the last", ret.isAfterLast()); + assertFalse(ret.isLast(), "should not be the last"); + assertTrue(ret.isAfterLast(), "should be after the last"); } } } - @Test - public void testUpdateCountOnCopyCmd() throws Exception { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testUpdateCountOnCopyCmd(String queryResultFormat) throws Exception { + try (Statement statement = createStatement(queryResultFormat)) { try { statement.execute("create or replace table testcopy(cola string)"); @@ -1001,16 +1027,18 @@ public void testUpdateCountOnCopyCmd() throws Exception { } } - @Test - public void testGetTimeNullTimestampAndTimestampNullTime() throws Throwable { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetTimeNullTimestampAndTimestampNullTime(String queryResultFormat) + throws Throwable { + try (Statement statement = createStatement(queryResultFormat)) { try { statement.execute("create or replace table testnullts(c1 timestamp, c2 time)"); statement.execute("insert into testnullts(c1, c2) values(null, null)"); try (ResultSet rs = statement.executeQuery("select * from testnullts")) { - assertTrue("should return result", rs.next()); - assertNull("return value must be null", rs.getTime(1)); - assertNull("return value must be null", rs.getTimestamp(2)); + assertTrue(rs.next(), "should return result"); + assertNull(rs.getTime(1), "return value must be null"); + assertNull(rs.getTimestamp(2), "return value must be null"); } } finally { statement.execute("drop table if exists testnullts"); @@ -1018,9 +1046,10 @@ public void testGetTimeNullTimestampAndTimestampNullTime() throws Throwable { } } - @Test - public void testNextNegative() throws SQLException { - try (ResultSet rs = connection.createStatement().executeQuery("select 1")) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testNextNegative(String queryResultFormat) throws SQLException { + try (ResultSet rs = createStatement(queryResultFormat).executeQuery("select 1")) { assertTrue(rs.next()); System.setProperty("snowflake.enable_incident_test2", "true"); try { @@ -1034,9 +1063,11 @@ public void testNextNegative() throws SQLException { } /** SNOW-1416051; Added in > 3.16.0 */ - @Test - public void shouldSerializeArrayAndObjectAsStringOnGetObject() throws SQLException { - try (Statement statement = connection.createStatement(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void shouldSerializeArrayAndObjectAsStringOnGetObject(String queryResultFormat) + throws SQLException { + try (Statement statement = createStatement(queryResultFormat); ResultSet resultSet = statement.executeQuery( "select ARRAY_CONSTRUCT(1,2,3), OBJECT_CONSTRUCT('a', 4, 'b', 'test')")) { diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java index 65cc27242..d8e3d111a 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowIT.java @@ -3,15 +3,15 @@ */ package net.snowflake.client.jdbc; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.math.BigDecimal; import java.nio.ByteBuffer; @@ -26,34 +26,21 @@ import java.util.List; import java.util.TimeZone; import java.util.stream.Collectors; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryArrow; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; import org.apache.arrow.vector.BigIntVector; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** Completely compare json and arrow resultSet behaviors */ -@RunWith(Parameterized.class) -@Category(TestCategoryArrow.class) +@Tag(TestTags.ARROW) public class ResultSetJsonVsArrowIT extends BaseJDBCTest { - @Parameterized.Parameters(name = "format={0}") - public static Object[][] data() { - // all tests in this class need to run for both query result formats json and arrow - return new Object[][] {{"JSON"}, {"Arrow"}}; - } - - protected String queryResultFormat; - - public ResultSetJsonVsArrowIT(String queryResultFormat) { - this.queryResultFormat = queryResultFormat; - } - public Connection init() throws SQLException { + public Connection init(String queryResultFormat) throws SQLException { Connection conn = getConnection(BaseJDBCTest.DONT_INJECT_SOCKET_TIMEOUT); try (Statement stmt = conn.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); @@ -61,9 +48,10 @@ public Connection init() throws SQLException { return conn; } - @Test - public void testGSResult() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGSResult(String queryResultFormat) throws SQLException { + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery( @@ -89,9 +77,10 @@ public void testGSResult() throws SQLException { } } - @Test - public void testGSResultReal() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGSResultReal(String queryResultFormat) throws SQLException { + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement()) { try { statement.execute("create or replace table t (a real)"); @@ -106,10 +95,11 @@ public void testGSResultReal() throws SQLException { } } - @Test - public void testGSResultScan() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGSResultScan(String queryResultFormat) throws SQLException { String queryId = null; - try (Connection con = init(); + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement()) { try { statement.execute("create or replace table t (a text)"); @@ -130,9 +120,10 @@ public void testGSResultScan() throws SQLException { } } - @Test - public void testGSResultForEmptyAndSmallTable() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGSResultForEmptyAndSmallTable(String queryResultFormat) throws SQLException { + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement()) { try { statement.execute("create or replace table t (a int)"); @@ -150,9 +141,10 @@ public void testGSResultForEmptyAndSmallTable() throws SQLException { } } - @Test - public void testSNOW89737() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSNOW89737(String queryResultFormat) throws SQLException { + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement()) { try { statement.execute( @@ -203,9 +195,10 @@ public void testSNOW89737() throws SQLException { * * @throws SQLException */ - @Test - public void testSemiStructuredData() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSemiStructuredData(String queryResultFormat) throws SQLException { + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery( @@ -240,10 +233,11 @@ public void testSemiStructuredData() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testStructuredTypes() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testStructuredTypes(String queryResultFormat) throws SQLException { + try (Connection con = init(queryResultFormat); Statement stmt = con.createStatement()) { stmt.execute("alter session set feature_structured_types = 'ENABLED';"); @@ -263,8 +257,9 @@ public void testStructuredTypes() throws SQLException { } } - private Connection init(String table, String column, String values) throws SQLException { - Connection con = init(); + private Connection init(String queryResultFormat, String table, String column, String values) + throws SQLException { + Connection con = init(queryResultFormat); try (Statement statement = con.createStatement()) { statement.execute("create or replace table " + table + " " + column); statement.execute("insert into " + table + " values " + values); @@ -272,7 +267,7 @@ private Connection init(String table, String column, String values) throws SQLEx return con; } - private boolean isJSON() { + private boolean isJSON(String queryResultFormat) { return queryResultFormat.equalsIgnoreCase("json"); } @@ -287,13 +282,14 @@ private boolean isJSON() { * * @throws SQLException */ - @Test - public void testTinyInt() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testTinyInt(String queryResultFormat) throws SQLException { int[] cases = {0, 1, -1, 127, -128}; String table = "test_arrow_tiny_int"; String column = "(a int)"; String values = "(" + StringUtils.join(ArrayUtils.toObject(cases), "),(") + "), (NULL)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -349,13 +345,14 @@ public void testTinyInt() throws SQLException { * * @throws SQLException */ - @Test - public void testScaledTinyInt() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testScaledTinyInt(String queryResultFormat) throws SQLException { float[] cases = {0.0f, 0.11f, -0.11f, 1.27f, -1.28f}; String table = "test_arrow_tiny_int"; String column = "(a number(3,2))"; String values = "(" + StringUtils.join(ArrayUtils.toObject(cases), "),(") + "), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = con.createStatement().executeQuery("select * from test_arrow_tiny_int")) { try { @@ -396,7 +393,7 @@ public void testScaledTinyInt() throws SQLException { assertEquals(val, rs.getDouble(1), delta); assertEquals(new BigDecimal(rs.getString(1)), rs.getBigDecimal(1)); assertEquals(rs.getBigDecimal(1), rs.getObject(1)); - if (isJSON()) { + if (isJSON(queryResultFormat)) { try { rs.getByte(1); fail(); @@ -408,7 +405,7 @@ public void testScaledTinyInt() throws SQLException { assertEquals(((byte) (cases[i] * 100)), rs.getByte(1)); } - if (!isJSON()) { + if (!isJSON(queryResultFormat)) { byte[] bytes = new byte[1]; bytes[0] = rs.getByte(1); assertArrayEquals(bytes, rs.getBytes(1)); @@ -446,13 +443,14 @@ public void testScaledTinyInt() throws SQLException { * * @throws SQLException */ - @Test - public void testSmallInt() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSmallInt(String queryResultFormat) throws SQLException { short[] cases = {0, 1, -1, 127, -128, 128, -129, 32767, -32768}; String table = "test_arrow_small_int"; String column = "(a int)"; String values = "(" + StringUtils.join(ArrayUtils.toObject(cases), "),(") + "), (NULL)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -477,7 +475,7 @@ public void testSmallInt() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -490,7 +488,7 @@ public void testSmallInt() throws SQLException { } ByteBuffer bb = ByteBuffer.allocate(2); bb.putShort(cases[i]); - if (isJSON()) { + if (isJSON(queryResultFormat)) { byte[] res = rs.getBytes(1); for (int j = res.length - 1; j >= 0; j--) { assertEquals(bb.array()[2 - res.length + j], res[j]); @@ -531,14 +529,15 @@ public void testSmallInt() throws SQLException { * * @throws SQLException */ - @Test - public void testScaledSmallInt() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testScaledSmallInt(String queryResultFormat) throws SQLException { float[] cases = {0, 2.0f, -2.0f, 32.767f, -32.768f}; short[] shortCompact = {0, 2000, -2000, 32767, -32768}; String table = "test_arrow_small_int"; String column = "(a number(5,3))"; String values = "(" + StringUtils.join(ArrayUtils.toObject(cases), "),(") + "), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = con.createStatement().executeQuery("select * from test_arrow_small_int")) { try { @@ -583,7 +582,7 @@ public void testScaledSmallInt() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -598,7 +597,7 @@ public void testScaledSmallInt() throws SQLException { byteBuffer.putShort(shortCompact[i]); assertArrayEquals(byteBuffer.array(), rs.getBytes(1)); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { SQLException se = (SQLException) e; assertEquals( (int) ErrorCode.INVALID_VALUE_CONVERT.getMessageCode(), se.getErrorCode()); @@ -639,15 +638,16 @@ public void testScaledSmallInt() throws SQLException { * * @throws SQLException */ - @Test - public void testInt() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testInt(String queryResultFormat) throws SQLException { int[] cases = { 0, 1, -1, 127, -128, 128, -129, 32767, -32768, 32768, -32769, 2147483647, -2147483648 }; String table = "test_arrow_int"; String column = "(a int)"; String values = "(" + StringUtils.join(ArrayUtils.toObject(cases), "),(") + "), (NULL)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = con.createStatement().executeQuery("select * from " + table)) { try { @@ -686,7 +686,7 @@ public void testInt() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -699,7 +699,7 @@ public void testInt() throws SQLException { } ByteBuffer bb = ByteBuffer.allocate(4); bb.putInt(cases[i]); - if (isJSON()) { + if (isJSON(queryResultFormat)) { byte[] res = rs.getBytes(1); for (int j = res.length - 1; j >= 0; j--) { assertEquals(bb.array()[4 - res.length + j], res[j]); @@ -740,8 +740,9 @@ public void testInt() throws SQLException { * * @throws SQLException */ - @Test - public void testScaledInt() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testScaledInt(String queryResultFormat) throws SQLException { int scale = 9; int[] intCompacts = {0, 123456789, -123456789, 2147483647, -2147483647}; List caseList = @@ -755,7 +756,7 @@ public void testScaledInt() throws SQLException { String column = String.format("(a number(10,%d))", scale); String values = "(" + StringUtils.join(cases, "),(") + "), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = con.createStatement().executeQuery("select * from test_arrow_int")) { try { @@ -800,7 +801,7 @@ public void testScaledInt() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -815,7 +816,7 @@ public void testScaledInt() throws SQLException { byteBuffer.putInt(intCompacts[i]); assertArrayEquals(byteBuffer.array(), rs.getBytes(1)); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { SQLException se = (SQLException) e; assertEquals( (int) ErrorCode.INVALID_VALUE_CONVERT.getMessageCode(), se.getErrorCode()); @@ -856,8 +857,9 @@ public void testScaledInt() throws SQLException { * * @throws SQLException */ - @Test - public void testBigInt() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testBigInt(String queryResultFormat) throws SQLException { long[] cases = { 0, 1, @@ -880,7 +882,7 @@ public void testBigInt() throws SQLException { String table = "test_arrow_big_int"; String column = "(a int)"; String values = "(" + StringUtils.join(ArrayUtils.toObject(cases), "),(") + "), (NULL)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -934,7 +936,7 @@ public void testBigInt() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -984,8 +986,9 @@ public void testBigInt() throws SQLException { * * @throws SQLException */ - @Test - public void testScaledBigInt() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testScaledBigInt(String queryResultFormat) throws SQLException { int scale = 18; long[] longCompacts = { 0, 123456789, -123456789, 2147483647, -2147483647, Long.MIN_VALUE, Long.MAX_VALUE @@ -1001,7 +1004,7 @@ public void testScaledBigInt() throws SQLException { String column = String.format("(a number(38,%d))", scale); String values = "(" + StringUtils.join(cases, "),(") + "), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -1046,7 +1049,7 @@ public void testScaledBigInt() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -1061,7 +1064,7 @@ public void testScaledBigInt() throws SQLException { byteBuffer.putLong(longCompacts[i]); assertArrayEquals(byteBuffer.array(), rs.getBytes(1)); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { SQLException se = (SQLException) e; assertEquals( (int) ErrorCode.INVALID_VALUE_CONVERT.getMessageCode(), se.getErrorCode()); @@ -1103,8 +1106,9 @@ public void testScaledBigInt() throws SQLException { * * @throws SQLException */ - @Test - public void testDecimalNoScale() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testDecimalNoScale(String queryResultFormat) throws SQLException { int scale = 0; String[] longCompacts = { "10000000000000000000000000000000000000", @@ -1120,7 +1124,7 @@ public void testDecimalNoScale() throws SQLException { String column = String.format("(a number(38,%d))", scale); String values = "(" + StringUtils.join(cases, "),(") + "), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -1166,7 +1170,7 @@ public void testDecimalNoScale() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -1212,8 +1216,9 @@ public void testDecimalNoScale() throws SQLException { * * @throws SQLException */ - @Test - public void testDecimalWithLargeScale() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testDecimalWithLargeScale(String queryResultFormat) throws SQLException { int scale = 37; String[] longCompacts = { "1.0000000000000000000000000000000000000", @@ -1229,7 +1234,7 @@ public void testDecimalWithLargeScale() throws SQLException { String column = String.format("(a number(38,%d))", scale); String values = "(" + StringUtils.join(cases, "),(") + "), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { @@ -1274,7 +1279,7 @@ public void testDecimalWithLargeScale() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -1287,7 +1292,7 @@ public void testDecimalWithLargeScale() throws SQLException { try { assertArrayEquals(cases[i].toBigInteger().toByteArray(), rs.getBytes(1)); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { SQLException se = (SQLException) e; assertEquals( (int) ErrorCode.INVALID_VALUE_CONVERT.getMessageCode(), se.getErrorCode()); @@ -1329,9 +1334,10 @@ public void testDecimalWithLargeScale() throws SQLException { * * @throws SQLException */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testDecimal() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testDecimal(String queryResultFormat) throws SQLException { int scale = 37; long[] longCompacts = { 0, 123456789, -123456789, 2147483647, -2147483647, Long.MIN_VALUE, Long.MAX_VALUE @@ -1347,7 +1353,7 @@ public void testDecimal() throws SQLException { String column = String.format("(a number(38,%d))", scale); String values = "(" + StringUtils.join(cases, "),(") + "), (null)"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = con.createStatement().executeQuery("select * from " + table)) { try { @@ -1393,7 +1399,7 @@ public void testDecimal() throws SQLException { rs.getByte(1); fail(); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { // Note: not caught by SQLException! assertTrue(e.toString().contains("NumberFormatException")); } else { @@ -1406,7 +1412,7 @@ public void testDecimal() throws SQLException { try { assertArrayEquals(byteBuf.putLong(0, longCompacts[i]).array(), rs.getBytes(1)); } catch (Exception e) { - if (isJSON()) { + if (isJSON(queryResultFormat)) { SQLException se = (SQLException) e; assertEquals( (int) ErrorCode.INVALID_VALUE_CONVERT.getMessageCode(), se.getErrorCode()); @@ -1440,8 +1446,9 @@ public void testDecimal() throws SQLException { * * @throws SQLException */ - @Test - public void testDoublePrecision() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testDoublePrecision(String queryResultFormat) throws SQLException { String[] cases = { // SNOW-31249 "-86.6426540296895", @@ -1470,12 +1477,12 @@ public void testDoublePrecision() throws SQLException { String column = "(a double)"; String values = "(" + StringUtils.join(cases, "),(") + ")"; - try (Connection con = init(table, column, values); + try (Connection con = init(queryResultFormat, table, column, values); Statement statement = con.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { try { int i = 0; - if (isJSON()) { + if (isJSON(queryResultFormat)) { while (rs.next()) { assertEquals(json_results[i++], Double.toString(rs.getDouble(1))); } @@ -1491,12 +1498,13 @@ public void testDoublePrecision() throws SQLException { } } - @Test - public void testBoolean() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testBoolean(String queryResultFormat) throws SQLException { String table = "test_arrow_boolean"; String column = "(a boolean)"; String values = "(true),(null),(false)"; - try (Connection conn = init(table, column, values); + try (Connection conn = init(queryResultFormat, table, column, values); Statement statement = conn.createStatement(); ResultSet rs = statement.executeQuery("select * from " + table)) { assertTrue(rs.next()); @@ -1512,12 +1520,13 @@ public void testBoolean() throws SQLException { } } - @Test - public void testClientSideSorting() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testClientSideSorting(String queryResultFormat) throws SQLException { String table = "test_arrow_sort_on"; String column = "( a int, b double, c string)"; String values = "(1,2.0,'test'),(0,2.0, 'test'),(1,2.0,'abc')"; - try (Connection conn = init(table, column, values); + try (Connection conn = init(queryResultFormat, table, column, values); Statement statement = conn.createStatement()) { try { // turn on sorting mode @@ -1537,9 +1546,10 @@ public void testClientSideSorting() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testClientSideSortingOnBatchedChunk() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testClientSideSortingOnBatchedChunk(String queryResultFormat) throws SQLException { // in this test, the first chunk contains multiple batches when the format is Arrow String[] queries = { "set-sf-property sort on", @@ -1557,7 +1567,7 @@ public void testClientSideSortingOnBatchedChunk() throws SQLException { "insert into T values (3);", }; - try (Connection conn = init(); + try (Connection conn = init(queryResultFormat); Statement stat = conn.createStatement()) { try { for (String q : queries) { @@ -1580,9 +1590,10 @@ public void testClientSideSortingOnBatchedChunk() throws SQLException { } } - @Test - public void testTimestampNTZAreAllNulls() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testTimestampNTZAreAllNulls(String queryResultFormat) throws SQLException { + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement()) { try { statement.executeQuery( @@ -1600,10 +1611,11 @@ public void testTimestampNTZAreAllNulls() throws SQLException { } } - @Test - public void TestArrowStringRoundTrip() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void TestArrowStringRoundTrip(String queryResultFormat) throws SQLException { String big_number = "11111111112222222222333333333344444444"; - try (Connection con = init(); + try (Connection con = init(queryResultFormat); Statement st = con.createStatement()) { try { for (int i = 0; i < 38; i++) { @@ -1625,10 +1637,11 @@ public void TestArrowStringRoundTrip() throws SQLException { } } - @Test - public void TestArrowFloatRoundTrip() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void TestArrowFloatRoundTrip(String queryResultFormat) throws SQLException { float[] cases = {Float.MAX_VALUE, Float.MIN_VALUE}; - try (Connection con = init(); + try (Connection con = init(queryResultFormat); Statement st = con.createStatement()) { try { for (float f : cases) { @@ -1645,12 +1658,13 @@ public void TestArrowFloatRoundTrip() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void TestTimestampNTZWithDLS() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void TestTimestampNTZWithDLS(String queryResultFormat) throws SQLException { TimeZone origTz = TimeZone.getDefault(); String[] timeZones = new String[] {"America/New_York", "America/Los_Angeles"}; - try (Connection con = init(); + try (Connection con = init(queryResultFormat); Statement st = con.createStatement()) { for (String timeZone : timeZones) { TimeZone.setDefault(TimeZone.getTimeZone(timeZone)); @@ -1751,10 +1765,11 @@ public void TestTimestampNTZWithDLS() throws SQLException { } } - @Test - public void TestTimestampNTZBinding() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void TestTimestampNTZBinding(String queryResultFormat) throws SQLException { TimeZone origTz = TimeZone.getDefault(); - try (Connection con = init()) { + try (Connection con = init(queryResultFormat)) { TimeZone.setDefault(TimeZone.getTimeZone("PST")); try (Statement st = con.createStatement()) { st.execute("alter session set CLIENT_TIMESTAMP_TYPE_MAPPING=TIMESTAMP_NTZ"); diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowMultiTZIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowMultiTZIT.java index f62e7701c..d89cebfc6 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowMultiTZIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetJsonVsArrowMultiTZIT.java @@ -3,46 +3,51 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import net.snowflake.client.category.TestCategoryArrow; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.ProvidersUtil; +import net.snowflake.client.providers.ScaleProvider; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import net.snowflake.client.providers.SnowflakeArgumentsProvider; +import net.snowflake.client.providers.TimezoneProvider; import org.apache.commons.lang3.StringUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsSource; /** Completely compare json and arrow resultSet behaviors */ -@RunWith(Parameterized.class) -@Category(TestCategoryArrow.class) +@Tag(TestTags.ARROW) public class ResultSetJsonVsArrowMultiTZIT extends BaseJDBCWithSharedConnectionIT { - @Parameterized.Parameters(name = "format={0}, tz={1}") - public static Collection data() { - // all tests in this class need to run for both query result formats json and arrow - String[] timeZones = new String[] {"UTC", "America/New_York", "Asia/Singapore"}; - String[] queryFormats = new String[] {"json", "arrow"}; - List ret = new ArrayList<>(); - for (String queryFormat : queryFormats) { - for (String timeZone : timeZones) { - ret.add(new Object[] {queryFormat, timeZone}); - } + static String originalTz; + + static class DataProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return ProvidersUtil.cartesianProduct( + context, new SimpleResultFormatProvider(), new TimezoneProvider(3)); } - return ret; } - private final String queryResultFormat; - private final String tz; + static class DataWithScaleProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return ProvidersUtil.cartesianProduct(context, new DataProvider(), new ScaleProvider()); + } + } - @Before + @BeforeEach public void setSessionTimezone() throws SQLException { try (Statement statement = connection.createStatement()) { statement.execute( @@ -56,13 +61,26 @@ public void setSessionTimezone() throws SQLException { } } - public ResultSetJsonVsArrowMultiTZIT(String queryResultFormat, String timeZone) { - this.queryResultFormat = queryResultFormat; - System.setProperty("user.timezone", timeZone); - tz = timeZone; + private static void setTimezone(String tz) { + System.setProperty("user.timezone", tz); + } + + @BeforeAll + public static void saveTimezone() { + originalTz = System.getProperty("user.timezone"); + } + + @AfterAll + public static void restoreTimezone() { + if (originalTz != null) { + System.setProperty("user.timezone", originalTz); + } else { + System.clearProperty("user.timezone"); + } } - private void init(String table, String column, String values) throws SQLException { + private void init(String table, String column, String values, String queryResultFormat) + throws SQLException { try (Statement statement = connection.createStatement()) { statement.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); statement.execute("create or replace table " + table + " " + column); @@ -70,8 +88,10 @@ private void init(String table, String column, String values) throws SQLExceptio } } - @Test - public void testTime() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataWithScaleProvider.class) + public void testTime(String queryResultFormat, String tz, int scale) throws SQLException { + setTimezone(tz); String[] times = { "00:01:23", "00:01:23.1", @@ -84,13 +104,13 @@ public void testTime() throws SQLException { "00:01:23.12345678", "00:01:23.123456789" }; - for (int scale = 0; scale <= 9; scale++) { - testTimeWithScale(times, scale); - } + testTimeWithScale(times, scale, queryResultFormat); } - @Test - public void testDate() throws Exception { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testDate(String queryResultFormat, String tz) throws Exception { + setTimezone(tz); String[] cases = { "2017-01-01", "2014-01-02", @@ -108,8 +128,8 @@ public void testDate() throws Exception { String column = "(a date)"; String values = "('" + StringUtils.join(cases, "'),('") + "'), (null)"; - init(table, column, values); - try (Statement statement = connection.createStatement()) { + init(table, column, values, queryResultFormat); + try (Statement statement = createStatement(queryResultFormat)) { try (ResultSet rs = statement.executeQuery("select * from " + table)) { int i = 0; while (i < cases.length) { @@ -129,12 +149,13 @@ public void testDate() throws Exception { } } - public void testTimeWithScale(String[] times, int scale) throws SQLException { + public void testTimeWithScale(String[] times, int scale, String queryResultFormat) + throws SQLException { String table = "test_arrow_time"; String column = "(a time(" + scale + "))"; String values = "('" + StringUtils.join(times, "'),('") + "'), (null)"; - init(table, column, values); - try (Statement statement = connection.createStatement(); + init(table, column, values, queryResultFormat); + try (Statement statement = createStatement(queryResultFormat); ResultSet rs = statement.executeQuery("select * from " + table)) { for (int i = 0; i < times.length; i++) { assertTrue(rs.next()); @@ -146,14 +167,11 @@ public void testTimeWithScale(String[] times, int scale) throws SQLException { } } - @Test - public void testTimestampNTZ() throws SQLException { - for (int scale = 0; scale <= 9; scale++) { - testTimestampNTZWithScale(scale); - } - } - - public void testTimestampNTZWithScale(int scale) throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataWithScaleProvider.class) + public void testTimestampNTZWithScale(String queryResultFormat, String tz, int scale) + throws SQLException { + setTimezone(tz); String[] cases = { "2017-01-01 12:00:00", "2014-01-02 16:00:00", @@ -181,8 +199,8 @@ public void testTimestampNTZWithScale(int scale) throws SQLException { String column = "(a timestamp_ntz(" + scale + "))"; String values = "('" + StringUtils.join(cases, "'),('") + "'), (null)"; - init(table, column, values); - try (Statement statement = connection.createStatement()) { + init(table, column, values, queryResultFormat); + try (Statement statement = createStatement(queryResultFormat)) { try (ResultSet rs = statement.executeQuery("select * from " + table)) { int i = 0; while (i < cases.length) { @@ -193,12 +211,13 @@ public void testTimestampNTZWithScale(int scale) throws SQLException { assertNull(rs.getString(1)); } statement.execute("drop table " + table); - System.clearProperty("user.timezone"); } } - @Test - public void testTimestampNTZWithNanos() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampNTZWithNanos(String queryResultFormat, String tz) throws SQLException { + setTimezone(tz); String[] cases = { "2017-01-01 12:00:00.123456789", "2014-01-02 16:00:00.0123", @@ -215,8 +234,8 @@ public void testTimestampNTZWithNanos() throws SQLException { String column = "(a timestamp_ntz)"; String values = "('" + StringUtils.join(cases, "'),('") + "'), (null)"; - init(table, column, values); - try (Statement statement = connection.createStatement()) { + init(table, column, values, queryResultFormat); + try (Statement statement = createStatement(queryResultFormat)) { try (ResultSet rs = statement.executeQuery("select * from " + table)) { int i = 0; while (i < cases.length) { @@ -227,7 +246,6 @@ public void testTimestampNTZWithNanos() throws SQLException { assertNull(rs.getString(1)); } finally { statement.execute("drop table " + table); - System.clearProperty("user.timezone"); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java index dc16d5dcf..ab7a6e081 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetLatestIT.java @@ -6,12 +6,12 @@ import static net.snowflake.client.TestUtil.expectSnowflakeLoggedFeatureNotSupportedException; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.fasterxml.jackson.databind.JsonNode; import java.lang.reflect.Field; @@ -43,10 +43,9 @@ import java.util.TimeZone; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryResultSet; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ObjectMapperFactory; import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SessionUtil; @@ -55,10 +54,14 @@ import net.snowflake.client.jdbc.telemetry.TelemetryData; import net.snowflake.client.jdbc.telemetry.TelemetryField; import net.snowflake.client.jdbc.telemetry.TelemetryUtil; +import net.snowflake.client.providers.SimpleResultFormatProvider; import net.snowflake.common.core.SFBinary; import org.apache.arrow.vector.Float8Vector; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** * ResultSet integration tests for the latest JDBC driver. This doesn't work for the oldest @@ -66,15 +69,11 @@ * if the tests still is not applicable. If it is applicable, move tests to ResultSetIT so that both * the latest and oldest supported driver run the tests. */ -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSetLatestIT extends ResultSet0IT { - - public ResultSetLatestIT() { - this("json"); - } - - ResultSetLatestIT(String queryResultFormat) { - super(queryResultFormat); + private static void setQueryResultFormat(Statement stmt, String queryResultFormat) + throws SQLException { + stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); } private String createTableSql = @@ -91,10 +90,10 @@ public ResultSetLatestIT() { * * @throws Throwable */ - @Test - public void testMemoryClearingAfterInterrupt() throws Throwable { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testMemoryClearingAfterInterrupt(String queryResultFormat) throws Throwable { + try (Statement statement = createStatement(queryResultFormat)) { final long initialMemoryUsage = SnowflakeChunkDownloader.getCurrentMemoryUsage(); try { // Inject an InterruptedException into the SnowflakeChunkDownloader.terminate() function @@ -128,12 +127,12 @@ public void testMemoryClearingAfterInterrupt() throws Throwable { * multiple statements concurrently uses a lot of memory. This checks that chunks download even * when there is not enough memory available for concurrent prefetching. */ - @Test - public void testChunkDownloaderNoHang() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testChunkDownloaderNoHang(String queryResultFormat) throws SQLException { int stmtCount = 30; int rowCount = 170000; - try (Connection connection = getConnection(); - Statement stmt = connection.createStatement()) { + try (Statement stmt = createStatement(queryResultFormat)) { List rsList = new ArrayList<>(); // Set memory limit to low number connection @@ -165,12 +164,12 @@ public void testChunkDownloaderNoHang() throws SQLException { } /** This tests that the SnowflakeChunkDownloader doesn't hang when memory limits are low. */ - @Test - public void testChunkDownloaderSetRetry() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testChunkDownloaderSetRetry(String queryResultFormat) throws SQLException { int stmtCount = 3; int rowCount = 170000; - try (Connection connection = getConnection(); - Statement stmt = connection.createStatement()) { + try (Statement stmt = createStatement(queryResultFormat)) { connection .unwrap(SnowflakeConnectionV1.class) .getSFBaseSession() @@ -214,9 +213,12 @@ public void testChunkDownloaderSetRetry() throws SQLException { * @throws ExecutionException arises if error occurred when sending telemetry events * @throws InterruptedException arises if error occurred when sending telemetry events */ - @Test - public void testMetadataAPIMetricCollection() + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testMetadataAPIMetricCollection(String queryResultFormat) throws SQLException, ExecutionException, InterruptedException { + Statement stmt = createStatement(queryResultFormat); + stmt.close(); Telemetry telemetry = connection.unwrap(SnowflakeConnectionV1.class).getSfSession().getTelemetryClient(); DatabaseMetaData metadata = connection.getMetaData(); @@ -276,9 +278,10 @@ public void testMetadataAPIMetricCollection() * * @throws SQLException */ - @Test - public void testGetCharacterStreamNull() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetCharacterStreamNull(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute("create or replace table JDBC_NULL_CHARSTREAM (col1 varchar(16))"); statement.execute("insert into JDBC_NULL_CHARSTREAM values(NULL)"); try (ResultSet rs = statement.executeQuery("select * from JDBC_NULL_CHARSTREAM")) { @@ -293,9 +296,10 @@ public void testGetCharacterStreamNull() throws SQLException { * * @throws SQLException arises if any exception occurs */ - @Test - public void testMultipleChunks() throws Exception { - try (Statement statement = connection.createStatement(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testMultipleChunks(String queryResultFormat) throws Exception { + try (Statement statement = createStatement(queryResultFormat); // 10000 rows should be enough to force result into multiple chunks ResultSet resultSet = @@ -345,10 +349,11 @@ public void testMultipleChunks() throws Exception { * * @throws SQLException arises if any exception occurs */ - @Test - public void testResultSetMetadata() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testResultSetMetadata(String queryResultFormat) throws SQLException { final Map params = getConnectionParameters(); - try (Statement statement = connection.createStatement()) { + try (Statement statement = createStatement(queryResultFormat)) { try { statement.execute("create or replace table test_rsmd(colA number(20, 5), colB string)"); statement.execute("insert into test_rsmd values(1.00, 'str'),(2.00, 'str2')"); @@ -396,9 +401,10 @@ public void testResultSetMetadata() throws SQLException { * * @throws SQLException */ - @Test - public void testEmptyResultSet() throws SQLException { - try (Statement statement = connection.createStatement(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testEmptyResultSet(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat); // the only function that returns ResultSetV1.emptyResultSet() ResultSet rs = statement.getGeneratedKeys()) { assertFalse(rs.next()); @@ -505,9 +511,10 @@ public void testEmptyResultSet() throws SQLException { * * @throws Exception arises if any exception occurs. */ - @Test - public void testBytesCrossTypeTests() throws Exception { - try (ResultSet resultSet = numberCrossTesting()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testBytesCrossTypeTests(String queryResultFormat) throws Exception { + try (ResultSet resultSet = numberCrossTesting(queryResultFormat)) { assertTrue(resultSet.next()); // assert that 0 is returned for null values for every type of value for (int i = 1; i < 13; i++) { @@ -538,9 +545,11 @@ public void testBytesCrossTypeTests() throws Exception { // SNOW-204185 // 30s for timeout. This test usually finishes in around 10s. - @Test(timeout = 30000) - public void testResultChunkDownloaderException() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @Timeout(30) + public void testResultChunkDownloaderException(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { // The generated resultSet must be big enough for triggering result chunk downloader String query = @@ -578,8 +587,7 @@ public void testResultChunkDownloaderException() throws SQLException { */ @Test public void testGetObjectWithBigInt() throws SQLException { - try (Statement statement = connection.createStatement()) { - statement.execute("alter session set jdbc_query_result_format ='json'"); + try (Statement statement = createStatement("json")) { // test with greatest possible number and greatest negative possible number String[] extremeNumbers = { "99999999999999999999999999999999999999", "-99999999999999999999999999999999999999" @@ -608,9 +616,10 @@ private byte[] floatToByteArray(float i) { * * @throws SQLException */ - @Test - public void testGetBigDecimalWithScale() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetBigDecimalWithScale(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute("create or replace table test_get(colA number(38,9))"); try (PreparedStatement preparedStatement = connection.prepareStatement("insert into test_get values(?)")) { @@ -634,11 +643,13 @@ public void testGetBigDecimalWithScale() throws SQLException { } } - @Test - public void testGetDataTypeWithTimestampTz() throws Exception { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetDataTypeWithTimestampTz(String queryResultFormat) throws Exception { try (Connection connection = getConnection()) { ResultSetMetaData resultSetMetaData = null; try (Statement statement = connection.createStatement()) { + setQueryResultFormat(statement, queryResultFormat); statement.executeQuery("create or replace table ts_test(ts timestamp_tz)"); try (ResultSet resultSet = statement.executeQuery("select * from ts_test")) { resultSetMetaData = resultSet.getMetaData(); @@ -669,13 +680,14 @@ public void testGetDataTypeWithTimestampTz() throws Exception { * * @throws SQLException */ - @Test - public void testGetEmptyOrNullClob() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetEmptyOrNullClob(String queryResultFormat) throws SQLException { Clob clob = connection.createClob(); clob.setString(1, "hello world"); Clob emptyClob = connection.createClob(); emptyClob.setString(1, ""); - try (Statement statement = connection.createStatement()) { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute( "create or replace table test_get_clob(colA varchar, colNull varchar, colEmpty text)"); try (PreparedStatement preparedStatement = @@ -703,10 +715,11 @@ public void testGetEmptyOrNullClob() throws SQLException { * * @throws SQLException */ - @Test - public void testSetNullClob() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testSetNullClob(String queryResultFormat) throws SQLException { Clob clob = null; - try (Statement statement = connection.createStatement()) { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute("create or replace table test_set_clob(colNull varchar)"); try (PreparedStatement preparedStatement = connection.prepareStatement("insert into test_set_clob values(?)")) { @@ -722,12 +735,14 @@ public void testSetNullClob() throws SQLException { } } - @Test - public void testCallStatementType() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testCallStatementType(String queryResultFormat) throws SQLException { Properties props = new Properties(); props.put("USE_STATEMENT_TYPE_CALL_FOR_STORED_PROC_CALLS", "true"); try (Connection connection = getConnection(props); Statement statement = connection.createStatement()) { + setQueryResultFormat(statement, queryResultFormat); try { String sp = "CREATE OR REPLACE PROCEDURE \"SP_ZSDLEADTIME_ARCHIVE_DAILY\"()\n" @@ -793,9 +808,10 @@ public void testCallStatementType() throws SQLException { * Test that new query error message function for checking async query error messages is not * implemented for synchronous queries * */ - @Test - public void testNewFeaturesNotSupportedExeceptions() throws SQLException { - try (Statement statement = connection.createStatement(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testNewFeaturesNotSupportedExeceptions(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat); ResultSet rs = statement.executeQuery("select 1")) { expectSnowflakeLoggedFeatureNotSupportedException( rs.unwrap(SnowflakeResultSet.class)::getQueryErrorMessage); @@ -841,9 +857,10 @@ public void testNewFeaturesNotSupportedExeceptions() throws SQLException { } } - @Test - public void testInvalidUnWrap() throws SQLException { - try (ResultSet rs = connection.createStatement().executeQuery("select 1")) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testInvalidUnWrap(String queryResultFormat) throws SQLException { + try (ResultSet rs = createStatement(queryResultFormat).executeQuery("select 1")) { try { rs.unwrap(SnowflakeUtil.class); } catch (SQLException ex) { @@ -856,9 +873,8 @@ public void testInvalidUnWrap() throws SQLException { @Test public void testGetObjectJsonResult() throws SQLException { - try (Statement statement = connection.createStatement()) { + try (Statement statement = createStatement("json")) { try { - statement.execute("alter session set jdbc_query_result_format ='json'"); statement.execute("create or replace table testObj (colA double, colB boolean)"); try (PreparedStatement preparedStatement = @@ -878,9 +894,10 @@ public void testGetObjectJsonResult() throws SQLException { } } - @Test - public void testMetadataIsCaseSensitive() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testMetadataIsCaseSensitive(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { String sampleCreateTableWithAllColTypes = "CREATE or replace TABLE case_sensitive (" @@ -929,14 +946,14 @@ public void testMetadataIsCaseSensitive() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testAutoIncrementJsonResult() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testAutoIncrementResult(String queryResultFormat) throws SQLException { Properties paramProperties = new Properties(); paramProperties.put("ENABLE_FIX_759900", true); - try (Connection connection = init(paramProperties); + try (Connection connection = init(paramProperties, queryResultFormat); Statement statement = connection.createStatement()) { - statement.execute("alter session set jdbc_query_result_format ='json'"); statement.execute( "create or replace table auto_inc(id int autoincrement, name varchar(10), another_col int autoincrement)"); @@ -953,34 +970,11 @@ public void testAutoIncrementJsonResult() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testAutoIncrementArrowResult() throws SQLException { - Properties paramProperties = new Properties(); - paramProperties.put("ENABLE_FIX_759900", true); - try (Connection connection = init(paramProperties); - Statement statement = connection.createStatement()) { - statement.execute("alter session set jdbc_query_result_format ='arrow'"); - - statement.execute( - "create or replace table auto_inc(id int autoincrement, name varchar(10), another_col int autoincrement)"); - statement.execute("insert into auto_inc(name) values('test1')"); - - try (ResultSet resultSet = statement.executeQuery("select * from auto_inc")) { - assertTrue(resultSet.next()); - - ResultSetMetaData metaData = resultSet.getMetaData(); - assertTrue(metaData.isAutoIncrement(1)); - assertFalse(metaData.isAutoIncrement(2)); - assertTrue(metaData.isAutoIncrement(3)); - } - } - } - - @Test - public void testGranularTimeFunctionsInSessionTimezone() throws SQLException { - try (Connection connection = getConnection(); - Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGranularTimeFunctionsInSessionTimezone(String queryResultFormat) + throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { try { statement.execute("create or replace table testGranularTime(t time)"); statement.execute("insert into testGranularTime values ('10:10:10')"); @@ -997,39 +991,43 @@ public void testGranularTimeFunctionsInSessionTimezone() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testGranularTimeFunctionsInUTC() throws SQLException { - try (Connection connection = getConnection()) { - TimeZone origTz = TimeZone.getDefault(); - try (Statement statement = connection.createStatement()) { - try { - TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); - statement.execute("alter session set JDBC_USE_SESSION_TIMEZONE=false"); - statement.execute("create or replace table testGranularTime(t time)"); - statement.execute("insert into testGranularTime values ('10:10:10')"); - try (ResultSet resultSet = statement.executeQuery("select * from testGranularTime")) { - assertTrue(resultSet.next()); - assertEquals(Time.valueOf("02:10:10"), resultSet.getTime(1)); - assertEquals(02, resultSet.getTime(1).getHours()); - assertEquals(10, resultSet.getTime(1).getMinutes()); - assertEquals(10, resultSet.getTime(1).getSeconds()); - } - } finally { - TimeZone.setDefault(origTz); - statement.execute("drop table if exists testGranularTime"); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testGranularTimeFunctionsInUTC(String queryResultFormat) throws SQLException { + TimeZone origTz = TimeZone.getDefault(); + try (Statement statement = createStatement(queryResultFormat)) { + try { + TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); + statement.execute("alter session set JDBC_USE_SESSION_TIMEZONE=false"); + statement.execute("create or replace table testGranularTime(t time)"); + statement.execute("insert into testGranularTime values ('10:10:10')"); + try (ResultSet resultSet = statement.executeQuery("select * from testGranularTime")) { + assertTrue(resultSet.next()); + assertEquals(Time.valueOf("02:10:10"), resultSet.getTime(1)); + assertEquals(02, resultSet.getTime(1).getHours()); + assertEquals(10, resultSet.getTime(1).getMinutes()); + assertEquals(10, resultSet.getTime(1).getSeconds()); } + } finally { + TimeZone.setDefault(origTz); + statement.execute("drop table if exists testGranularTime"); } } } /** Added in > 3.14.5 */ - @Test - public void testLargeStringRetrieval() throws SQLException { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testLargeStringRetrieval(String queryResultFormat) throws SQLException { + String originalMaxJsonStringLength = + System.getProperty(ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM); + System.clearProperty(ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM); String tableName = "maxJsonStringLength_table"; int colLength = 16777216; try (Connection con = getConnection(); Statement statement = con.createStatement()) { + setQueryResultFormat(statement, queryResultFormat); SFBaseSession session = con.unwrap(SnowflakeConnectionV1.class).getSFBaseSession(); Integer maxVarcharSize = (Integer) session.getOtherParameter("VARCHAR_AND_BINARY_MAX_SIZE_IN_RESULT"); @@ -1039,7 +1037,6 @@ public void testLargeStringRetrieval() throws SQLException { statement.execute("create or replace table " + tableName + " (c1 string(" + colLength + "))"); statement.execute( "insert into " + tableName + " select randstr(" + colLength + ", random())"); - assertNull(System.getProperty(ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM)); try (ResultSet rs = statement.executeQuery("select * from " + tableName)) { assertTrue(rs.next()); assertEquals(colLength, rs.getString(1).length()); @@ -1047,25 +1044,30 @@ public void testLargeStringRetrieval() throws SQLException { } } catch (Exception e) { fail("executeQuery should not fail"); + } finally { + if (originalMaxJsonStringLength != null) { + System.setProperty( + ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM, originalMaxJsonStringLength); + } } } private static void assertAllColumnsAreLongButBigIntIsBigDecimal(ResultSet rs) throws SQLException { while (rs.next()) { - assertEquals(java.lang.Long.class, rs.getObject(1).getClass()); - assertEquals(java.math.BigDecimal.class, rs.getObject(2).getClass()); - assertEquals(java.lang.Long.class, rs.getObject(3).getClass()); - assertEquals(java.lang.Long.class, rs.getObject(4).getClass()); + assertEquals(Long.class, rs.getObject(1).getClass()); + assertEquals(BigDecimal.class, rs.getObject(2).getClass()); + assertEquals(Long.class, rs.getObject(3).getClass()); + assertEquals(Long.class, rs.getObject(4).getClass()); } } private static void assertAllColumnsAreBigDecimal(ResultSet rs) throws SQLException { while (rs.next()) { - assertEquals(java.math.BigDecimal.class, rs.getObject(1).getClass()); - assertEquals(java.math.BigDecimal.class, rs.getObject(2).getClass()); - assertEquals(java.math.BigDecimal.class, rs.getObject(3).getClass()); - assertEquals(java.math.BigDecimal.class, rs.getObject(4).getClass()); + assertEquals(BigDecimal.class, rs.getObject(1).getClass()); + assertEquals(BigDecimal.class, rs.getObject(2).getClass()); + assertEquals(BigDecimal.class, rs.getObject(3).getClass()); + assertEquals(BigDecimal.class, rs.getObject(4).getClass()); } } @@ -1140,9 +1142,10 @@ public void testGetObjectForJSONResultFormatUsingJDBCDecimalAsInt() throws SQLEx } } - @Test - public void testGetObjectWithType() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetObjectWithType(String queryResultFormat) throws SQLException { + try (Statement statement = createStatement(queryResultFormat)) { statement.execute( " CREATE OR REPLACE TABLE test_all_types (" + " string VARCHAR, " diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetMultiTimeZoneIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetMultiTimeZoneIT.java index c0a494613..93266290c 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetMultiTimeZoneIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetMultiTimeZoneIT.java @@ -5,11 +5,11 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; import java.sql.Date; @@ -22,46 +22,74 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collection; import java.util.List; -import java.util.Properties; import java.util.TimeZone; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import java.util.stream.Stream; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; /** Test ResultSet */ -@RunWith(Parameterized.class) -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSetMultiTimeZoneIT extends BaseJDBCTest { - @Parameterized.Parameters(name = "format={0}, tz={1}") - public static Collection data() { - // all tests in this class need to run for both query result formats json and arrow - String[] timeZones = new String[] {"UTC", "Asia/Singapore", "MEZ"}; - String[] queryFormats = new String[] {"json", "arrow"}; - List ret = new ArrayList<>(); - for (String queryFormat : queryFormats) { - for (String timeZone : timeZones) { - ret.add(new Object[] {queryFormat, timeZone}); + static TimeZone ogTz; + + static class DataProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + List timezones = + new ArrayList() { + { + add("UTC"); + add("Asia/Singapore"); + add("CET"); + } + }; + List queryFormats = + new ArrayList() { + { + add("json"); + add("arrow"); + } + }; + + List args = new ArrayList<>(); + for (String timeZone : timezones) { + for (String queryFormat : queryFormats) { + args.add(Arguments.argumentSet(timeZone + " " + queryFormat, timeZone, queryFormat)); + } } + + return args.stream(); } - return ret; } - private final String queryResultFormat; + @BeforeAll + public static void setDefaultTimezone() { + ogTz = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } - public ResultSetMultiTimeZoneIT(String queryResultFormat, String timeZone) { - this.queryResultFormat = queryResultFormat; + private static void setTimezone(String timeZone) { System.setProperty("user.timezone", timeZone); } - public Connection init() throws SQLException { + @AfterAll + public static void clearTimezone() { + TimeZone.setDefault(ogTz); + System.clearProperty("user.timezone"); + } + + public Connection init(String queryResultFormat) throws SQLException { Connection connection = BaseJDBCTest.getConnection(); try (Statement statement = connection.createStatement()) { @@ -78,15 +106,23 @@ public Connection init() throws SQLException { return connection; } - public Connection init(Properties paramProperties) throws SQLException { - Connection conn = getConnection(DONT_INJECT_SOCKET_TIMEOUT, paramProperties, false, false); - try (Statement stmt = conn.createStatement()) { - stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); + public Connection init() throws SQLException { + Connection connection = BaseJDBCTest.getConnection(); + + try (Statement statement = connection.createStatement()) { + statement.execute( + "alter session set " + + "TIMEZONE='America/Los_Angeles'," + + "TIMESTAMP_TYPE_MAPPING='TIMESTAMP_LTZ'," + + "TIMESTAMP_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'," + + "TIMESTAMP_TZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'," + + "TIMESTAMP_LTZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'," + + "TIMESTAMP_NTZ_OUTPUT_FORMAT='DY, DD MON YYYY HH24:MI:SS TZHTZM'"); } - return conn; + return connection; } - @Before + @BeforeEach public void setUp() throws SQLException { try (Connection con = init(); Statement statement = con.createStatement()) { @@ -107,21 +143,21 @@ public void setUp() throws SQLException { + "error_on_column_count_mismatch=false)"); // put files assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%orders_jdbc")); + "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%orders_jdbc"), + "Failed to put a file"); assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE_2) + " @%orders_jdbc")); + "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE_2) + " @%orders_jdbc"), + "Failed to put a file"); int numRows = statement.executeUpdate("copy into orders_jdbc"); - assertEquals("Unexpected number of rows copied: " + numRows, 73, numRows); + assertEquals(73, numRows, "Unexpected number of rows copied: " + numRows); } } - @After + @AfterEach public void tearDown() throws SQLException { System.clearProperty("user.timezone"); try (Connection con = init(); @@ -131,10 +167,12 @@ public void tearDown() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testGetDateAndTime() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testGetDateAndTime(String tz, String queryResultFormat) throws SQLException { + setTimezone(tz); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("create or replace table dateTime(colA Date, colB Timestamp, colC Time)"); @@ -189,11 +227,13 @@ public void testGetDateAndTime() throws SQLException { } // SNOW-25029: The driver should reduce Time milliseconds mod 24h. - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testTimeRange() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testTimeRange(String tz, String queryResultFormat) throws SQLException { + setTimezone(tz); final String insertTime = "insert into timeTest values (?), (?), (?), (?)"; - try (Connection connection = init(); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("create or replace table timeTest (c1 time)"); @@ -243,11 +283,13 @@ public void testTimeRange() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testCurrentTime() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testCurrentTime(String tz, String queryResultFormat) throws SQLException { + setTimezone(tz); final String insertTime = "insert into datetime values (?, ?, ?)"; - try (Connection connection = init()) { + try (Connection connection = init(queryResultFormat)) { assertFalse(connection.createStatement().execute("alter session set TIMEZONE='UTC'")); @@ -285,10 +327,12 @@ public void testCurrentTime() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testBindTimestampTZ() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testBindTimestampTZ(String tz, String queryResultFormat) throws SQLException { + setTimezone(tz); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute( @@ -315,10 +359,12 @@ public void testBindTimestampTZ() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testGetOldDate() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testGetOldDate(String tz, String queryResultFormat) throws SQLException { + setTimezone(tz); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute("create or replace table testOldDate(d date)"); @@ -353,9 +399,11 @@ public void testGetOldDate() throws SQLException { } } - @Test - public void testGetStringForDates() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testGetStringForDates(String tz, String queryResultFormat) throws SQLException { + setTimezone(tz); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { String expectedDate1 = "2020-08-01"; String expectedDate2 = "1920-11-11"; @@ -370,10 +418,13 @@ public void testGetStringForDates() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testDateTimeRelatedTypeConversion() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testDateTimeRelatedTypeConversion(String tz, String queryResultFormat) + throws SQLException { + setTimezone(tz); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute( @@ -437,10 +488,12 @@ public void testDateTimeRelatedTypeConversion() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testGetOldTimestamp() throws SQLException { - try (Connection con = init(); + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testGetOldTimestamp(String tz, String queryResultFormat) throws SQLException { + setTimezone(tz); + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement()) { try { statement.execute("create or replace table testOldTs(cola timestamp_ntz)"); @@ -464,12 +517,14 @@ public void testGetOldTimestamp() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testPrepareOldTimestamp() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testPrepareOldTimestamp(String tz, String queryResultFormat) throws SQLException { + setTimezone(tz); TimeZone origTz = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); - try (Connection con = init(); + try (Connection con = init(queryResultFormat); Statement statement = con.createStatement()) { try { statement.execute("create or replace table testPrepOldTs(cola timestamp_ntz, colb date)"); diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetMultiTimeZoneLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetMultiTimeZoneLatestIT.java index e03dc35df..2d0bbd6b6 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetMultiTimeZoneLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetMultiTimeZoneLatestIT.java @@ -1,7 +1,7 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Date; import java.sql.PreparedStatement; @@ -10,51 +10,67 @@ import java.sql.Statement; import java.sql.Timestamp; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Calendar; -import java.util.Collection; import java.util.List; import java.util.TimeZone; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.BooleanProvider; +import net.snowflake.client.providers.ProvidersUtil; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import net.snowflake.client.providers.SnowflakeArgumentsProvider; +import net.snowflake.client.providers.TimezoneProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsSource; /** * ResultSet multi timezone tests for the latest JDBC driver. This cannot run for the old driver. */ -@RunWith(Parameterized.class) -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class ResultSetMultiTimeZoneLatestIT extends BaseJDBCWithSharedConnectionIT { - @Parameterized.Parameters(name = "format={0}, tz={1}") - public static Collection data() { - // all tests in this class need to run for both query result formats json and arrow - // UTC and Europe/London have different offsets during daylight savings time so it is important - // to test both to ensure daylight savings time is correct - String[] timeZones = new String[] {"UTC", "Asia/Singapore", "MEZ", "Europe/London"}; - String[] queryFormats = new String[] {"json", "arrow"}; - List ret = new ArrayList<>(); - for (String queryFormat : queryFormats) { - for (String timeZone : timeZones) { - ret.add(new Object[] {queryFormat, timeZone}); - } + + private static String originalTz; + + private static class DataProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return ProvidersUtil.cartesianProduct( + context, new SimpleResultFormatProvider(), new TimezoneProvider(4)); } - return ret; } - private final String queryResultFormat; + private static class DataWithFlagProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return ProvidersUtil.cartesianProduct(context, new DataProvider(), new BooleanProvider()); + } + } + + @BeforeAll + public static void saveTimezone() { + originalTz = System.getProperty("user.timezone"); + } + + @AfterAll + public static void restoreTimezone() { + if (originalTz != null) { + System.setProperty("user.timezone", originalTz); + } else { + System.clearProperty("user.timezone"); + } + } - public ResultSetMultiTimeZoneLatestIT(String queryResultFormat, String timeZone) { - this.queryResultFormat = queryResultFormat; - System.setProperty("user.timezone", timeZone); + private static void setTimezone(String tz) { + System.setProperty("user.timezone", tz); } - @Before - public void init() throws SQLException { + public void init(String queryResultFormat, String tz) throws SQLException { + setTimezone(tz); try (Statement statement = connection.createStatement()) { statement.execute( "alter session set " @@ -74,9 +90,11 @@ public void init() throws SQLException { * * @throws SQLException */ - @Test - public void testTimesWithGetTimestamp() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimesWithGetTimestamp(String queryResultFormat, String tz) throws SQLException { + init(queryResultFormat, tz); + try (Statement statement = createStatement(queryResultFormat)) { String timeStringValue = "10:30:50.123456789"; String timestampStringValue = "1970-01-01 " + timeStringValue; int length = timestampStringValue.length(); @@ -108,9 +126,12 @@ public void testTimesWithGetTimestamp() throws SQLException { * * @throws SQLException */ - @Test - public void testTimestampNTZWithDaylightSavings() throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void testTimestampNTZWithDaylightSavings(String queryResultFormat, String tz) + throws SQLException { + init(queryResultFormat, tz); + try (Statement statement = createStatement(queryResultFormat)) { statement.execute( "alter session set TIMESTAMP_TYPE_MAPPING='TIMESTAMP_NTZ'," + "TIMEZONE='Europe/London'"); try (ResultSet rs = statement.executeQuery("select TIMESTAMP '2011-09-04 00:00:00'")) { @@ -125,13 +146,18 @@ public void testTimestampNTZWithDaylightSavings() throws SQLException { * Test for getDate(int columnIndex, Calendar cal) function to ensure it matches values with * getTimestamp function */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testDateAndTimestampWithTimezone() throws SQLException { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + @DontRunOnGithubActions + public void testDateAndTimestampWithTimezone(String queryResultFormat, String tz) + throws SQLException { + init(queryResultFormat, tz); Calendar cal = null; SimpleDateFormat sdf = null; - - try (Statement statement = connection.createStatement()) { + // The following line allows for the tests to work locally. This should be removed when the + // tests are properly fixed. + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + try (Statement statement = createStatement(queryResultFormat)) { statement.execute("alter session set JDBC_FORMAT_DATE_WITH_TIMEZONE=true"); try (ResultSet rs = statement.executeQuery( @@ -186,31 +212,6 @@ public void testDateAndTimestampWithTimezone() throws SQLException { } } - /** - * Tests that formats are correct when JDBC_USE_SESSION_TIMEZONE=true and other related time/date - * formatting parameters are at their default values - * - * @throws SQLException - */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testSessionTimezoneUsage() throws SQLException { - testUseSessionTimeZoneHelper(true); - } - - /** - * Tests that the new param overrides previous time/date/timestamp formatting parameters such as - * JDBC_TREAT_TIMESTAMP_NTZ_AS_UTC, CLIENT_HONOR_CLIENT_TZ_FOR_TIMESTAMP_NTZ, and - * JDBC_FORMAT_DATE_WITH_TIMEZONE. - * - * @throws SQLException - */ - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testUseSessionTimeZoneOverrides() throws SQLException { - testUseSessionTimeZoneHelper(false); - } - /** * Helper function to test behavior of parameter JDBC_USE_SESSION_TIMEZONE. When * JDBC_USE_SESSION_TIMEZONE=true, time/date/timestamp values are displayed using the session @@ -226,8 +227,12 @@ public void testUseSessionTimeZoneOverrides() throws SQLException { * parameters * @throws SQLException */ - private void testUseSessionTimeZoneHelper(boolean useDefaultParamSettings) throws SQLException { - try (Statement statement = connection.createStatement()) { + @ParameterizedTest + @ArgumentsSource(DataWithFlagProvider.class) + public void testUseSessionTimeZoneHelper( + String queryResultFormat, String tz, boolean useDefaultParamSettings) throws SQLException { + init(queryResultFormat, tz); + try (Statement statement = createStatement(queryResultFormat)) { try { // create table with all timestamp types, time, and date statement.execute( diff --git a/src/test/java/net/snowflake/client/jdbc/ResultSetVectorLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ResultSetVectorLatestIT.java index bc553b2f6..23cdc5b6b 100644 --- a/src/test/java/net/snowflake/client/jdbc/ResultSetVectorLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ResultSetVectorLatestIT.java @@ -1,9 +1,10 @@ package net.snowflake.client.jdbc; import static net.snowflake.client.jdbc.SnowflakeUtil.EXTRA_TYPES_VECTOR; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -12,11 +13,11 @@ import java.sql.Types; import java.util.Arrays; import java.util.List; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** * ResultSet integration tests for the latest JDBC driver. This doesn't work for the oldest @@ -25,69 +26,60 @@ * If it is applicable, move tests to ResultSetVectorIT so that both the latest and oldest supported * driver run the tests. */ -@Category(TestCategoryResultSet.class) -@RunWith(Parameterized.class) +@Tag(TestTags.RESULT_SET) public class ResultSetVectorLatestIT extends ResultSet0IT { - private final String queryResultFormat; - - public ResultSetVectorLatestIT(String queryResultFormat) { - super(queryResultFormat); - this.queryResultFormat = queryResultFormat; - } - - @Parameterized.Parameters(name = "format={0}") - public static List queryResultFormats() { - return Arrays.asList("json", "arrow"); - } - - @Test - public void testGetIntVectorAsIntArray() throws SQLException { - try (Statement stmt = connection.createStatement()) { - enforceQueryResultFormat(stmt); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetIntVectorAsIntArray(String queryResultFormat) throws SQLException { + try (Statement stmt = createStatement(queryResultFormat)) { + enforceQueryResultFormat(stmt, queryResultFormat); Integer[] vector = {-1, 5}; try (ResultSet resultSet = stmt.executeQuery("select " + vectorToString(vector, "int"))) { assertTrue(resultSet.next()); Integer[] result = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, Integer.class); - assertEquals(vector, result); + assertArrayEquals(vector, result); assertVectorMetadata(resultSet, 1, Types.INTEGER, 1); } } } - @Test - public void testGetIntVectorAsLongArray() throws SQLException { - try (Statement stmt = connection.createStatement()) { - enforceQueryResultFormat(stmt); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetIntVectorAsLongArray(String queryResultFormat) throws SQLException { + try (Statement stmt = createStatement(queryResultFormat)) { + enforceQueryResultFormat(stmt, queryResultFormat); Long[] vector = {-1L, 5L}; try (ResultSet resultSet = stmt.executeQuery("select " + vectorToString(vector, "int"))) { assertTrue(resultSet.next()); Long[] result = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, Long.class); - assertEquals(vector, result); + assertArrayEquals(vector, result); assertVectorMetadata(resultSet, 1, Types.INTEGER, 1); } } } - @Test - public void testGetFloatVectorAsFloatArray() throws SQLException { - try (Statement stmt = connection.createStatement()) { - enforceQueryResultFormat(stmt); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetFloatVectorAsFloatArray(String queryResultFormat) throws SQLException { + try (Statement stmt = createStatement(queryResultFormat)) { + enforceQueryResultFormat(stmt, queryResultFormat); Float[] vector = {-1.2f, 5.1f, 15.87f}; try (ResultSet resultSet = stmt.executeQuery("select " + vectorToString(vector, "float"))) { assertTrue(resultSet.next()); Float[] result = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, Float.class); - assertEquals(vector, result); + assertArrayEquals(vector, result); assertVectorMetadata(resultSet, 1, Types.FLOAT, 1); } } } - @Test - public void testGetNullAsIntVector() throws SQLException { - try (Statement stmt = connection.createStatement()) { - enforceQueryResultFormat(stmt); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetNullAsIntVector(String queryResultFormat) throws SQLException { + try (Statement stmt = createStatement(queryResultFormat)) { + enforceQueryResultFormat(stmt, queryResultFormat); try (ResultSet resultSet = stmt.executeQuery("select null::vector(int, 2)")) { assertTrue(resultSet.next()); Integer[] result = @@ -98,10 +90,11 @@ public void testGetNullAsIntVector() throws SQLException { } } - @Test - public void testGetNullAsFloatVector() throws SQLException { - try (Statement stmt = connection.createStatement()) { - enforceQueryResultFormat(stmt); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetNullAsFloatVector(String queryResultFormat) throws SQLException { + try (Statement stmt = createStatement(queryResultFormat)) { + enforceQueryResultFormat(stmt, queryResultFormat); try (ResultSet resultSet = stmt.executeQuery("select null::vector(float, 2)")) { assertTrue(resultSet.next()); Integer[] result = @@ -112,42 +105,46 @@ public void testGetNullAsFloatVector() throws SQLException { } } - @Test - public void testGetIntVectorFromTable() throws SQLException { - try (Statement stmt = connection.createStatement()) { - enforceQueryResultFormat(stmt); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetIntVectorFromTable(String queryResultFormat) throws SQLException { + try (Statement stmt = createStatement(queryResultFormat)) { + enforceQueryResultFormat(stmt, queryResultFormat); stmt.execute("create or replace table test_vector_int(x vector(int, 2), y int)"); stmt.execute("insert into test_vector_int select [3, 7]::vector(int, 2), 15"); try (ResultSet resultSet = stmt.executeQuery("select x, y from test_vector_int")) { assertTrue(resultSet.next()); Integer[] result = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, Integer.class); - assertEquals(new Integer[] {3, 7}, result); + assertArrayEquals(new Integer[] {3, 7}, result); assertVectorMetadata(resultSet, 1, Types.INTEGER, 2); } } } - @Test - public void testGetFloatVectorFromTable() throws SQLException { - try (Statement stmt = connection.createStatement()) { - enforceQueryResultFormat(stmt); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetFloatVectorFromTable(String queryResultFormat) throws SQLException { + try (Statement stmt = createStatement(queryResultFormat)) { + enforceQueryResultFormat(stmt, queryResultFormat); stmt.execute("create or replace table test_vector_float(x vector(float, 2), y float)"); stmt.execute("insert into test_vector_float select [-3, 7.1]::vector(float, 2), 20.3"); try (ResultSet resultSet = stmt.executeQuery("select x, y from test_vector_float")) { assertTrue(resultSet.next()); Float[] result = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, Float.class); - assertEquals(new Float[] {-3f, 7.1f}, result); + assertArrayEquals(new Float[] {-3f, 7.1f}, result); assertVectorMetadata(resultSet, 1, Types.FLOAT, 2); } } } /** Added in > 3.16.1 */ - @Test - public void testGetVectorViaGetStringIsEqualToTheGetObject() throws SQLException { - try (Statement stmt = connection.createStatement()) { - enforceQueryResultFormat(stmt); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + public void testGetVectorViaGetStringIsEqualToTheGetObject(String queryResultFormat) + throws SQLException { + try (Statement stmt = createStatement(queryResultFormat)) { + enforceQueryResultFormat(stmt, queryResultFormat); Integer[] intVector = {-1, 5}; Float[] floatVector = {-1.2f, 5.1f, 15.87f}; try (ResultSet resultSet = @@ -164,7 +161,7 @@ public void testGetVectorViaGetStringIsEqualToTheGetObject() throws SQLException assertTrue(resultSet.next()); assertGetObjectAndGetStringBeTheSame(resultSet, "[-1,5]", 1); String floatArrayRepresentation = - "json".equals(queryResultFormat) + "json".equalsIgnoreCase(queryResultFormat) // in json we have slightly different format that we accept in the result ? "[-1.200000,5.100000,15.870000]" : "[-1.2,5.1,15.87]"; @@ -195,7 +192,8 @@ private String nullVectorToString(String vectorType) { return "null::vector(" + vectorType + ", 2)"; } - private void enforceQueryResultFormat(Statement stmt) throws SQLException { + private void enforceQueryResultFormat(Statement stmt, String queryResultFormat) + throws SQLException { String sql = String.format( "alter session set jdbc_query_result_format = '%s'", queryResultFormat.toUpperCase()); diff --git a/src/test/java/net/snowflake/client/jdbc/SSOConnectionTest.java b/src/test/java/net/snowflake/client/jdbc/SSOConnectionTest.java index 51c9179b4..6fbadd92f 100644 --- a/src/test/java/net/snowflake/client/jdbc/SSOConnectionTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SSOConnectionTest.java @@ -6,7 +6,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; @@ -38,7 +38,7 @@ import net.snowflake.common.core.ClientAuthnDTO; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.HttpPost; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; diff --git a/src/test/java/net/snowflake/client/jdbc/ServiceNameTest.java b/src/test/java/net/snowflake/client/jdbc/ServiceNameTest.java index bd51ef533..737cc1ffe 100644 --- a/src/test/java/net/snowflake/client/jdbc/ServiceNameTest.java +++ b/src/test/java/net/snowflake/client/jdbc/ServiceNameTest.java @@ -13,7 +13,7 @@ import net.snowflake.client.core.HttpUtil; import net.snowflake.client.core.SFSessionProperty; import org.apache.http.client.methods.HttpRequestBase; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; diff --git a/src/test/java/net/snowflake/client/jdbc/SessionUtilTest.java b/src/test/java/net/snowflake/client/jdbc/SessionUtilTest.java index a91fa4a89..4056dda1b 100644 --- a/src/test/java/net/snowflake/client/jdbc/SessionUtilTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SessionUtilTest.java @@ -1,11 +1,14 @@ package net.snowflake.client.jdbc; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; import net.snowflake.client.core.ObjectMapperFactory; import net.snowflake.client.core.SessionUtil; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SessionUtilTest { @Test @@ -16,56 +19,56 @@ public void testGetCommonParams() throws Exception { Map result = SessionUtil.getCommonParams( mapper.readTree("[{\"name\": \"testParam\", \"value\": true}]")); - Assert.assertTrue((boolean) result.get("testParam")); + assertTrue((boolean) result.get("testParam")); result = SessionUtil.getCommonParams( mapper.readTree("[{\"name\": \"testParam\", \"value\": false}]")); - Assert.assertFalse((boolean) result.get("testParam")); + assertFalse((boolean) result.get("testParam")); result = SessionUtil.getCommonParams(mapper.readTree("[{\"name\": \"testParam\", \"value\": 0}]")); - Assert.assertEquals(0, (int) result.get("testParam")); + assertEquals(0, (int) result.get("testParam")); result = SessionUtil.getCommonParams( mapper.readTree("[{\"name\": \"testParam\", \"value\": 1000}]")); - Assert.assertEquals(1000, (int) result.get("testParam")); + assertEquals(1000, (int) result.get("testParam")); result = SessionUtil.getCommonParams( mapper.readTree("[{\"name\": \"testParam\", \"value\": \"\"}]")); - Assert.assertEquals("", result.get("testParam")); + assertEquals("", result.get("testParam")); result = SessionUtil.getCommonParams( mapper.readTree("[{\"name\": \"testParam\", \"value\": \"value\"}]")); - Assert.assertEquals("value", result.get("testParam")); + assertEquals("value", result.get("testParam")); // Test known param name result = SessionUtil.getCommonParams( mapper.readTree("[{\"name\": \"CLIENT_DISABLE_INCIDENTS\", \"value\": true}]")); - Assert.assertTrue((boolean) result.get("CLIENT_DISABLE_INCIDENTS")); + assertTrue((boolean) result.get("CLIENT_DISABLE_INCIDENTS")); result = SessionUtil.getCommonParams( mapper.readTree("[{\"name\": \"CLIENT_DISABLE_INCIDENTS\", \"value\": false}]")); - Assert.assertFalse((boolean) result.get("CLIENT_DISABLE_INCIDENTS")); + assertFalse((boolean) result.get("CLIENT_DISABLE_INCIDENTS")); result = SessionUtil.getCommonParams( mapper.readTree( "[{\"name\": \"CLIENT_STAGE_ARRAY_BINDING_THRESHOLD\", \"value\": 0}]")); - Assert.assertEquals(0, (int) result.get("CLIENT_STAGE_ARRAY_BINDING_THRESHOLD")); + assertEquals(0, (int) result.get("CLIENT_STAGE_ARRAY_BINDING_THRESHOLD")); result = SessionUtil.getCommonParams( mapper.readTree( "[{\"name\": \"CLIENT_STAGE_ARRAY_BINDING_THRESHOLD\", \"value\": 1000}]")); - Assert.assertEquals(1000, (int) result.get("CLIENT_STAGE_ARRAY_BINDING_THRESHOLD")); + assertEquals(1000, (int) result.get("CLIENT_STAGE_ARRAY_BINDING_THRESHOLD")); result = SessionUtil.getCommonParams(mapper.readTree("[{\"name\": \"TIMEZONE\", \"value\": \"\"}]")); - Assert.assertEquals("", result.get("TIMEZONE")); + assertEquals("", result.get("TIMEZONE")); result = SessionUtil.getCommonParams( mapper.readTree("[{\"name\": \"TIMEZONE\", \"value\": \"value\"}]")); - Assert.assertEquals("value", result.get("TIMEZONE")); + assertEquals("value", result.get("TIMEZONE")); } } diff --git a/src/test/java/net/snowflake/client/jdbc/SessionVariablesIT.java b/src/test/java/net/snowflake/client/jdbc/SessionVariablesIT.java index 5a8d28922..c5f3c8a1f 100644 --- a/src/test/java/net/snowflake/client/jdbc/SessionVariablesIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SessionVariablesIT.java @@ -3,8 +3,8 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.sql.Connection; import java.sql.ResultSet; @@ -14,11 +14,11 @@ import java.util.Map; import java.util.Properties; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.category.TestCategoryOthers; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public final class SessionVariablesIT extends AbstractDriverIT { @Test public void testSettingSessionVariablesInConnectionProperties() throws SQLException { diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeAzureClientHandleExceptionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeAzureClientHandleExceptionLatestIT.java index c0c5dc18d..b57bdc86b 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeAzureClientHandleExceptionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeAzureClientHandleExceptionLatestIT.java @@ -3,6 +3,10 @@ */ package net.snowflake.client.jdbc; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.StorageExtendedErrorInformation; import java.io.File; @@ -13,26 +17,23 @@ import java.sql.SQLException; import java.sql.Statement; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.Constants; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.SFStatement; import net.snowflake.client.jdbc.cloud.storage.SnowflakeAzureClient; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.mockito.Mockito; /** Test for SnowflakeAzureClient handle exception function */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class SnowflakeAzureClientHandleExceptionLatestIT extends AbstractDriverIT { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; private Connection connection; private SFStatement sfStatement; private SFSession sfSession; @@ -41,7 +42,7 @@ public class SnowflakeAzureClientHandleExceptionLatestIT extends AbstractDriverI private int overMaxRetry; private int maxRetry; - @Before + @BeforeEach public void setup() throws SQLException { connection = getConnection("azureaccount"); sfSession = connection.unwrap(SnowflakeConnectionV1.class).getSfSession(); @@ -60,7 +61,7 @@ public void setup() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void error403RenewExpired() throws SQLException, InterruptedException { // Unauthenticated, renew is called. spyingClient.handleStorageException( @@ -101,99 +102,130 @@ public void run() { thread.start(); thread.interrupt(); thread.join(); - Assert.assertNull("Exception must not have been thrown in here", exceptionContainer[0]); + assertNull(exceptionContainer[0], "Exception must not have been thrown in here"); Mockito.verify(spyingClient, Mockito.times(4)).renew(Mockito.anyMap()); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void error403OverMaxRetryThrow() throws SQLException { - spyingClient.handleStorageException( - new StorageException( - "403", "Unauthenticated", 403, new StorageExtendedErrorInformation(), new Exception()), - overMaxRetry, - "upload", - sfSession, - command, - null); + @Test + @DontRunOnGithubActions + public void error403OverMaxRetryThrow() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new StorageException( + "403", + "Unauthenticated", + 403, + new StorageExtendedErrorInformation(), + new Exception()), + overMaxRetry, + "upload", + sfSession, + command, + null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void error403NullSession() throws SQLException { - spyingClient.handleStorageException( - new StorageException( - "403", "Unauthenticated", 403, new StorageExtendedErrorInformation(), new Exception()), - 0, - "upload", - null, - command, - null); + @Test + @DontRunOnGithubActions + public void error403NullSession() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new StorageException( + "403", + "Unauthenticated", + 403, + new StorageExtendedErrorInformation(), + new Exception()), + 0, + "upload", + null, + command, + null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorInvalidKey() throws SQLException { - spyingClient.handleStorageException( - new Exception(new InvalidKeyException()), 0, "upload", sfSession, command, null); + @Test + @DontRunOnGithubActions + public void errorInvalidKey() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new Exception(new InvalidKeyException()), 0, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @Test + @DontRunOnGithubActions public void errorInterruptedException() throws SQLException { // Can still retry, no error thrown try { spyingClient.handleStorageException( new InterruptedException(), 0, "upload", sfSession, command, null); } catch (Exception e) { - Assert.fail("Should not have exception here"); + fail("Should not have exception here"); } Mockito.verify(spyingClient, Mockito.never()).renew(Mockito.anyMap()); - spyingClient.handleStorageException( - new InterruptedException(), 26, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new InterruptedException(), 26, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorSocketTimeoutException() throws SQLException { + @Test + @DontRunOnGithubActions + public void errorSocketTimeoutException() throws SnowflakeSQLException { // Can still retry, no error thrown try { spyingClient.handleStorageException( new SocketTimeoutException(), 0, "upload", sfSession, command, null); } catch (Exception e) { - Assert.fail("Should not have exception here"); + fail("Should not have exception here"); } Mockito.verify(spyingClient, Mockito.never()).renew(Mockito.anyMap()); - spyingClient.handleStorageException( - new SocketTimeoutException(), 26, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new SocketTimeoutException(), 26, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorUnknownException() throws SQLException { - spyingClient.handleStorageException(new Exception(), 0, "upload", sfSession, command, null); + @Test + @DontRunOnGithubActions + public void errorUnknownException() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new Exception(), 0, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorNoSpaceLeftOnDevice() throws SQLException, IOException { - File destFolder = tmpFolder.newFolder(); + @Test + @DontRunOnGithubActions + public void errorNoSpaceLeftOnDevice() throws IOException { + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String getCommand = "get @testPutGet_stage/" + TEST_DATA_FILE + " 'file://" + destFolderCanonicalPath + "'"; - spyingClient.handleStorageException( - new StorageException( - "", - Constants.NO_SPACE_LEFT_ON_DEVICE_ERR, - new IOException(Constants.NO_SPACE_LEFT_ON_DEVICE_ERR)), - 0, - "download", - null, - getCommand, - null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new StorageException( + "", + Constants.NO_SPACE_LEFT_ON_DEVICE_ERR, + new IOException(Constants.NO_SPACE_LEFT_ON_DEVICE_ERR)), + 0, + "download", + null, + getCommand, + null)); } - @After + @AfterEach public void cleanUp() throws SQLException { sfStatement.close(); connection.close(); diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java index 8df351889..a2f4638b1 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeBasicDataSourceTest.java @@ -5,12 +5,12 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.SQLException; import java.util.Properties; import net.snowflake.client.core.SFSessionProperty; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Data source unit test */ public class SnowflakeBasicDataSourceTest { diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeChunkDownloaderLatestIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeChunkDownloaderLatestIT.java index 7dca1028e..0251f3984 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeChunkDownloaderLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeChunkDownloaderLatestIT.java @@ -3,7 +3,7 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; @@ -11,20 +11,20 @@ import java.sql.Statement; import java.util.List; import java.util.Properties; -import net.snowflake.client.category.TestCategoryCore; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class SnowflakeChunkDownloaderLatestIT extends BaseJDBCTest { private static String originalProxyHost; private static String originalProxyPort; private static String originalNonProxyHosts; - @BeforeClass + @BeforeAll public static void setUp() throws Exception { originalProxyHost = System.getProperty("https.proxyHost"); originalProxyPort = System.getProperty("https.proxyPort"); @@ -39,7 +39,7 @@ private static void restoreProperty(String key, String value) { } } - @AfterClass + @AfterAll public static void tearDown() throws Exception { restoreProperty("https.proxyHost", originalProxyHost); restoreProperty("https.proxyPort", originalProxyPort); diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeClobTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeClobTest.java index fa3d4de6e..b08221a41 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeClobTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeClobTest.java @@ -3,9 +3,9 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.io.InputStream; @@ -13,7 +13,7 @@ import java.io.Reader; import java.nio.charset.StandardCharsets; import java.sql.SQLException; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SnowflakeClobTest extends BaseJDBCTest { diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeConnectionV1Test.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeConnectionV1Test.java index a17a89b15..de6c4fb70 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeConnectionV1Test.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeConnectionV1Test.java @@ -6,7 +6,7 @@ import java.util.Map; import java.util.Properties; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Created by hyu on 2/2/18. */ public class SnowflakeConnectionV1Test { diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverConnectionStressTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverConnectionStressTest.java index 161e9c939..b50388d5d 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverConnectionStressTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverConnectionStressTest.java @@ -4,7 +4,7 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.sql.Connection; import java.sql.ResultSet; diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverIT.java index a540adcec..b245f8c0b 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverIT.java @@ -5,13 +5,14 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -24,6 +25,7 @@ import java.nio.channels.FileChannel; import java.sql.Connection; import java.sql.DatabaseMetaData; +import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -49,23 +51,21 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.RunningOnTestaccount; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.annotations.DontRunOnTestaccount; +import net.snowflake.client.category.TestTags; import net.snowflake.common.core.ClientAuthnDTO; import net.snowflake.common.core.SqlState; import org.apache.commons.io.FileUtils; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** General integration tests */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class SnowflakeDriverIT extends BaseJDBCTest { private static final int MAX_CONCURRENT_QUERIES_PER_USER = 50; private static final String getCurrenTransactionStmt = "SELECT CURRENT_TRANSACTION()"; @@ -73,15 +73,15 @@ public class SnowflakeDriverIT extends BaseJDBCTest { private static String ORDERS_JDBC = "ORDERS_JDBC"; - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; private ObjectMapper mapper = new ObjectMapper(); - @Rule public TemporaryFolder tmpFolder2 = new TemporaryFolder(); + @TempDir public File tmpFolder2; public String testStageName = String.format("test_stage_%s", UUID.randomUUID().toString()).replaceAll("-", "_"); - @BeforeClass + @BeforeAll public static void setUp() throws Throwable { try (Connection connection = getConnection()) { try (Statement statement = connection.createStatement()) { @@ -99,22 +99,22 @@ public static void setUp() throws Throwable { // put files assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%orders_jdbc")); + "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%orders_jdbc"), + "Failed to put a file"); assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE_2) + " @%orders_jdbc")); + "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE_2) + " @%orders_jdbc"), + "Failed to put a file"); int numRows = statement.executeUpdate("copy into orders_jdbc"); - assertEquals("Unexpected number of rows copied: " + numRows, 73, numRows); + assertEquals(73, numRows, "Unexpected number of rows copied: " + numRows); } } } - @AfterClass + @AfterAll public static void tearDown() throws SQLException { try (Connection connection = getConnection(); Statement statement = connection.createStatement()) { @@ -145,7 +145,7 @@ public static Connection getConnection() throws SQLException { /** Test connection to database using Snowflake Oauth instead of username/pw * */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testOauthConnection() throws SQLException { Map params = getConnectionParameters(); String role = null; @@ -182,7 +182,7 @@ public void testOauthConnection() throws SQLException { } } - @Ignore + @Disabled @Test public void testConnections() throws Throwable { ExecutorService executorService = Executors.newFixedThreadPool(MAX_CONCURRENT_QUERIES_PER_USER); @@ -237,7 +237,7 @@ public void testShowColumns() throws Throwable { try (Connection connection = getConnection(paramProperties); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("show columns in clustered_jdbc")) { - assertEquals("number of columns", 2, countRows(resultSet)); + assertEquals(2, countRows(resultSet), "number of columns"); } } @@ -259,7 +259,7 @@ public void testRowsPerResultset() throws Throwable { ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); int numColumns = resultSetMetaData.getColumnCount(); assertEquals(9, numColumns); - assertEquals("number of columns", 73, countRows(resultSet)); + assertEquals(73, countRows(resultSet), "number of columns"); } } } @@ -356,18 +356,18 @@ private void assertConstraintResults( // primary key for testConstraintsP1 should contain two rows for (int i = 0; i < numRows; i++) { - assertTrue("get constraint result row count", resultSet.next()); + assertTrue(resultSet.next(), "get constraint result row count"); if (pkTableName != null) { assertTrue( - "get constraint result primary table name", - pkTableName.equalsIgnoreCase(resultSet.getString(3))); + pkTableName.equalsIgnoreCase(resultSet.getString(3)), + "get constraint result primary table name"); } if (fkTableName != null) { assertTrue( - "get constraint result foreign table name", - fkTableName.equalsIgnoreCase(resultSet.getString(7))); + fkTableName.equalsIgnoreCase(resultSet.getString(7)), + "get constraint result foreign table name"); } } } @@ -513,8 +513,8 @@ public void testConstraints() throws Throwable { null, null, "TESTCONSTRAINTSP2", null, null, "TESTCONSTRAINTSF1"); assertFalse( - "cross reference from testConstraintsP2 to " + "testConstraintsF2 should be empty", - manualResultSet.next()); + manualResultSet.next(), + "cross reference from testConstraintsP2 to " + "testConstraintsF2 should be empty"); manualResultSet.close(); assertFalse(manualResultSet.next()); } finally { @@ -543,7 +543,7 @@ public void testQueryWithMaxRows() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testCancelQueryBySystemFunction() throws Throwable { try (Connection connection = getConnection(); Statement getSessionIdStmt = connection.createStatement()) { @@ -579,7 +579,7 @@ public void run() { fail("should raise an exception"); } catch (SQLException ex) { // assert the sqlstate is what we expect (QUERY CANCELLED) - assertEquals("sqlstate mismatch", SqlState.QUERY_CANCELED, ex.getSQLState()); + assertEquals(SqlState.QUERY_CANCELED, ex.getSQLState(), "sqlstate mismatch"); } } @@ -595,34 +595,34 @@ public void testDBMetadata() throws Throwable { // the following will issue try (ResultSet databaseSet = metaData.getCatalogs()) { - assertTrue("databases shouldn't be empty", databaseSet.next()); + assertTrue(databaseSet.next(), "databases shouldn't be empty"); // "show schemas in [databaseName]" ResultSet schemaSet = metaData.getSchemas(connection.getCatalog(), connection.getSchema()); - assertTrue("schemas shouldn't be empty", schemaSet.next()); + assertTrue(schemaSet.next(), "schemas shouldn't be empty"); assertTrue( - "database should be " + connection.getCatalog(), - connection.getCatalog().equalsIgnoreCase(schemaSet.getString(2))); + connection.getCatalog().equalsIgnoreCase(schemaSet.getString(2)), + "database should be " + connection.getCatalog()); assertTrue( - "schema should be " + connection.getSchema(), - connection.getSchema().equalsIgnoreCase(schemaSet.getString(1))); + connection.getSchema().equalsIgnoreCase(schemaSet.getString(1)), + "schema should be " + connection.getSchema()); // snow tables in a schema try (ResultSet tableSet = metaData.getTables( connection.getCatalog(), connection.getSchema(), ORDERS_JDBC, null)) { // types assertTrue( + tableSet.next(), String.format( "table %s should exists in db: %s, schema: %s", - ORDERS_JDBC, connection.getCatalog(), connection.getSchema()), - tableSet.next()); + ORDERS_JDBC, connection.getCatalog(), connection.getSchema())); assertTrue( - "database should be " + connection.getCatalog(), - connection.getCatalog().equalsIgnoreCase(schemaSet.getString(2))); + connection.getCatalog().equalsIgnoreCase(schemaSet.getString(2)), + "database should be " + connection.getCatalog()); assertTrue( - "schema should be " + connection.getSchema(), - connection.getSchema().equalsIgnoreCase(schemaSet.getString(1))); + connection.getSchema().equalsIgnoreCase(schemaSet.getString(1)), + "schema should be " + connection.getSchema()); assertTrue( - "table should be orders_jdbc", ORDERS_JDBC.equalsIgnoreCase(tableSet.getString(3))); + ORDERS_JDBC.equalsIgnoreCase(tableSet.getString(3)), "table should be orders_jdbc"); } } @@ -643,7 +643,7 @@ public void testDBMetadata() throws Throwable { assertTrue(ORDERS_JDBC.equalsIgnoreCase(tableMetaDataResultSet.getString(3))); ++cnt; } - assertEquals("number of tables", 1, cnt); + assertEquals(1, cnt, "number of tables"); } // test pattern try (ResultSet tableMetaDataResultSet = @@ -667,7 +667,7 @@ public void testDBMetadata() throws Throwable { break; } } - assertTrue("orders_jdbc not found", found); + assertTrue(found, "orders_jdbc not found"); } // get column metadata @@ -734,7 +734,7 @@ public void testDBMetadata() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutWithWildcardGCP() throws Throwable { Properties _connectionProperties = new Properties(); _connectionProperties.put("inject_wait_in_put", 5); @@ -748,21 +748,22 @@ public void testPutWithWildcardGCP() throws Throwable { // replace file name with wildcard character sourceFilePath = sourceFilePath.replace("orders_100.csv", "orders_10*.csv"); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; statement.execute("alter session set ENABLE_GCP_PUT_EXCEPTION_FOR_OLD_DRIVERS=false"); statement.execute("CREATE OR REPLACE STAGE wildcard_stage"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePath + " @wildcard_stage")); + statement.execute("PUT file://" + sourceFilePath + " @wildcard_stage"), + "Failed to put a file"); findFile(statement, "ls @wildcard_stage/"); assertTrue( - "Failed to get files", statement.execute( - "GET @wildcard_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @wildcard_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get files"); File downloaded; // download the files we just uploaded to stage @@ -808,23 +809,26 @@ private void copyContentFrom(File file1, File file2) throws Exception { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutGetLargeFileGCP() throws Throwable { try (Connection connection = getConnection("gcpaccount"); Statement statement = connection.createStatement()) { try { - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; - File largeTempFile = tmpFolder.newFile("largeFile.csv"); + File largeTempFile = new File(tmpFolder, "largeFile.csv"); + largeTempFile.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(largeTempFile))) { bw.write("Creating large test file for GCP PUT/GET test"); bw.write(System.lineSeparator()); bw.write("Creating large test file for GCP PUT/GET test"); bw.write(System.lineSeparator()); } - File largeTempFile2 = tmpFolder.newFile("largeFile2.csv"); + File largeTempFile2 = new File(tmpFolder, "largeFile2.csv"); + largeTempFile2.createNewFile(); String sourceFilePath = largeTempFile.getCanonicalPath(); @@ -840,8 +844,8 @@ public void testPutGetLargeFileGCP() throws Throwable { // create a stage to put the file in statement.execute("CREATE OR REPLACE STAGE largefile_stage"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePath + " @largefile_stage")); + statement.execute("PUT file://" + sourceFilePath + " @largefile_stage"), + "Failed to put a file"); // check that file exists in stage after PUT findFile(statement, "ls @largefile_stage/"); @@ -856,9 +860,9 @@ public void testPutGetLargeFileGCP() throws Throwable { // get file from new stage assertTrue( - "Failed to get files", statement.execute( - "GET @extra_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @extra_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get files"); // Make sure that the downloaded file exists; it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + "bigFile.csv.gz"); @@ -885,15 +889,17 @@ public void testPutGetLargeFileGCP() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutOverwrite() throws Throwable { // create 2 files: an original, and one that will overwrite the original - File file1 = tmpFolder.newFile("testfile.csv"); + File file1 = new File(tmpFolder, "testfile.csv"); + file1.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(file1))) { bw.write("Writing original file content. This should get overwritten."); } - File file2 = tmpFolder2.newFile("testfile.csv"); + File file2 = new File(tmpFolder2, "testfile.csv"); + file2.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(file2))) { bw.write("This is all new! This should be the result of the overwriting."); } @@ -901,7 +907,8 @@ public void testPutOverwrite() throws Throwable { String sourceFilePathOriginal = file1.getCanonicalPath(); String sourceFilePathOverwrite = file2.getCanonicalPath(); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; @@ -915,25 +922,25 @@ public void testPutOverwrite() throws Throwable { // create a stage to put the file in statement.execute("CREATE OR REPLACE STAGE testing_stage"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePathOriginal + " @testing_stage")); + statement.execute("PUT file://" + sourceFilePathOriginal + " @testing_stage"), + "Failed to put a file"); // check that file exists in stage after PUT findFile(statement, "ls @testing_stage/"); // put another file in same stage with same filename with overwrite = true assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + sourceFilePathOverwrite + " @testing_stage overwrite=true")); + "PUT file://" + sourceFilePathOverwrite + " @testing_stage overwrite=true"), + "Failed to put a file"); // check that file exists in stage after PUT findFile(statement, "ls @testing_stage/"); // get file from new stage assertTrue( - "Failed to get files", statement.execute( - "GET @testing_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @testing_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get files"); // Make sure that the downloaded file exists; it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + "testfile.csv.gz"); @@ -955,7 +962,7 @@ public void testPutOverwrite() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPut() throws Throwable { List accounts = Arrays.asList(null, "s3testaccount", "azureaccount", "gcpaccount"); @@ -971,11 +978,11 @@ public void testPut() throws Throwable { // put files assertTrue( - "Failed to put a file", statement.execute( "PUT file://" + getFullPathFileInResource(TEST_DATA_FILE) - + " @%testLoadToLocalFS/orders parallel=10")); + + " @%testLoadToLocalFS/orders parallel=10"), + "Failed to put a file"); try (ResultSet resultSet = statement.getResultSet()) { @@ -1042,16 +1049,16 @@ static void findFile(Statement statement, String checkSQL) throws Throwable { } // give enough time for s3 eventual consistency for US region Thread.sleep(1000); - assertTrue("Could not find a file", fileFound); + assertTrue(fileFound, "Could not find a file"); // assert the first column not null - assertNotNull("Null result", resultSet.getString(1)); + assertNotNull(resultSet.getString(1), "Null result"); } } } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testSQLError42S02() throws SQLException { try (Connection connection = getConnection(); @@ -1061,13 +1068,13 @@ public void testSQLError42S02() throws SQLException { fail("SQL exception not raised"); } catch (SQLException ex1) { // assert the sqlstate "42S02" which means BASE_TABLE_OR_VIEW_NOT_FOUND - assertEquals("sqlstate mismatch", "42S02", ex1.getSQLState()); + assertEquals("42S02", ex1.getSQLState(), "sqlstate mismatch"); } } } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testExplainPlan() throws Throwable { try (Connection connection = getConnection(); Statement statement = connection.createStatement(); @@ -1077,8 +1084,8 @@ public void testExplainPlan() throws Throwable { statement.executeQuery("EXPLAIN PLAN FOR SELECT c1 FROM orders_jdbc")) { ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); - assertTrue("must return more than 4 columns", resultSetMetaData.getColumnCount() >= 4); - assertTrue("must return more than 3 rows", countRows(resultSet) > 3); + assertTrue(resultSetMetaData.getColumnCount() >= 4, "must return more than 4 columns"); + assertTrue(countRows(resultSet) > 3, "must return more than 3 rows"); } } @@ -1175,7 +1182,7 @@ public void testUpdateCount() throws Throwable { int numRows = statement.executeUpdate("INSERT INTO testUpdateCount values (1, 'a'), (2, 'b')"); - assertEquals("Unexpected number of rows inserted: " + numRows, 2, numRows); + assertEquals(2, numRows, "Unexpected number of rows inserted: " + numRows); } finally { statement.execute("DROP TABLE if exists testUpdateCount"); } @@ -1202,7 +1209,7 @@ public void testSnow4245() throws Throwable { "insert into testSnow4245 values(NULL,NULL,NULL)," + "('2013-06-04 01:00:04','2013-06-04 01:00:04','2013-06-04 01:00:04')," + "('2013-06-05 23:00:05','2013-06-05 23:00:05','2013-06-05 23:00:05')"); - assertEquals("Unexpected number of rows inserted: " + numRows, 3, numRows); + assertEquals(3, numRows, "Unexpected number of rows inserted: " + numRows); // query the data try (ResultSet resultSet = @@ -1251,7 +1258,7 @@ public void testSnow4394() throws Throwable { int numRows = statement.executeUpdate( String.format("INSERT INTO %s(str) values('%s')", tableName, data)); - assertEquals("Unexpected number of rows inserted: " + numRows, 1, numRows); + assertEquals(1, numRows, "Unexpected number of rows inserted: " + numRows); try (ResultSet rset = statement.executeQuery(String.format("SELECT str FROM %s", tableName))) { @@ -1259,7 +1266,7 @@ public void testSnow4394() throws Throwable { while (rset.next()) { ret = rset.getString(1); } - assertEquals("Unexpected string value: " + ret, data, ret); + assertEquals(data, ret, "Unexpected string value: " + ret); } } finally { statement.execute(String.format("DROP TABLE if exists %s", tableName)); @@ -1318,8 +1325,8 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("integer", 1, resultSet.getInt(1)); - assertEquals("string", "hello", resultSet.getString(2)); + assertEquals(1, resultSet.getInt(1), "integer"); + assertEquals("hello", resultSet.getString(2), "string"); } // bind float preparedStatement.setDouble(1, 1.2); @@ -1332,8 +1339,8 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("double", 1.2, resultSet.getDouble(1), 0); - assertEquals("string", "hello", resultSet.getString(2)); + assertEquals(1.2, resultSet.getDouble(1), 0, "double"); + assertEquals("hello", resultSet.getString(2), "string"); } // bind string preparedStatement.setString(1, "hello"); @@ -1346,8 +1353,8 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("string1", "hello", resultSet.getString(1)); - assertEquals("string2", "hello", resultSet.getString(2)); + assertEquals("hello", resultSet.getString(1), "string1"); + assertEquals("hello", resultSet.getString(2), "string2"); } // bind date sqlDate = java.sql.Date.valueOf("2014-08-26"); @@ -1361,8 +1368,8 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("string", "2014-08-26", resultSet.getString(1)); - assertEquals("string", "hello", resultSet.getString(2)); + assertEquals("2014-08-26", resultSet.getString(1), "string"); + assertEquals("hello", resultSet.getString(2), "string"); } // bind timestamp ts = buildTimestamp(2014, 7, 26, 3, 52, 0, 0); @@ -1378,8 +1385,8 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); assertEquals( - "Incorrect timestamp", "Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(1)); - assertEquals("string", "hello", resultSet.getString(2)); + "Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(1), "Incorrect timestamp"); + assertEquals("hello", resultSet.getString(2), "string"); } // bind time tm = new Time(12345678); // 03:25:45.678 @@ -1393,8 +1400,8 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("Incorrect time", "03:25:45", resultSet.getString(1)); - assertEquals("string", "hello", resultSet.getString(2)); + assertEquals("03:25:45", resultSet.getString(1), "Incorrect time"); + assertEquals("hello", resultSet.getString(2), "string"); } } // bind in where clause @@ -1412,8 +1419,8 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("c1", "100", resultSet.getString(1)); - assertEquals("c2", "147004", resultSet.getString(2)); + assertEquals("100", resultSet.getString(1), "c1"); + assertEquals("147004", resultSet.getString(2), "c2"); } } @@ -1437,20 +1444,20 @@ public void testBind() throws Throwable { int rowCount = preparedStatement.executeUpdate(); // update count should be 1 - assertEquals("update count", 1, rowCount); + assertEquals(1, rowCount, "update count"); // test the inserted rows try (ResultSet resultSet = regularStatement.executeQuery("select * from testBind")) { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("int", 1, resultSet.getInt(1)); - assertEquals("string", "hello", resultSet.getString(2)); - assertEquals("double", 1.2, resultSet.getDouble(3), 0); - assertEquals("date", "2014-08-26", resultSet.getString(4)); - assertEquals("timestamp", "Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(5)); - assertEquals("time", "03:25:45", resultSet.getString(6)); - assertNull("date", resultSet.getString(7)); + assertEquals(1, resultSet.getInt(1), "int"); + assertEquals("hello", resultSet.getString(2), "string"); + assertEquals(1.2, resultSet.getDouble(3), 0, "double"); + assertEquals("2014-08-26", resultSet.getString(4), "date"); + assertEquals("Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(5), "timestamp"); + assertEquals("03:25:45", resultSet.getString(6), "time"); + assertNull(resultSet.getString(7), "date"); } } // bind in update statement @@ -1465,13 +1472,13 @@ public void testBind() throws Throwable { try (ResultSet resultSet = regularStatement.executeQuery("select * from testBind")) { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("int", 1, resultSet.getInt(1)); - assertEquals("string", "world", resultSet.getString(2)); - assertEquals("double", 1.2, resultSet.getDouble(3), 0); - assertEquals("date", "2014-08-26", resultSet.getString(4)); - assertEquals("timestamp", "Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(5)); - assertEquals("time", "03:25:45", resultSet.getString(6)); - assertNull("date", resultSet.getString(7)); + assertEquals(1, resultSet.getInt(1), "int"); + assertEquals("world", resultSet.getString(2), "string"); + assertEquals(1.2, resultSet.getDouble(3), 0, "double"); + assertEquals("2014-08-26", resultSet.getString(4), "date"); + assertEquals("Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(5), "timestamp"); + assertEquals("03:25:45", resultSet.getString(6), "time"); + assertNull(resultSet.getString(7), "date"); } // array bind for insert try (PreparedStatement preparedStatement = @@ -1499,11 +1506,11 @@ public void testBind() throws Throwable { // GS optimizes this into one insert execution, but we expand the // return count into an array - assertEquals("Number of update counts", 2, updateCounts.length); + assertEquals(2, updateCounts.length, "Number of update counts"); // update count should be 1 for each - assertEquals("update count", 1, updateCounts[0]); - assertEquals("update count", 1, updateCounts[1]); + assertEquals(1, updateCounts[0], "update count"); + assertEquals(1, updateCounts[1], "update count"); } // test the inserted rows try (ResultSet resultSet = @@ -1511,12 +1518,12 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("int", 2, resultSet.getInt(1)); - assertEquals("string", "hello", resultSet.getString(2)); - assertEquals("double", 1.2, resultSet.getDouble(3), 0); - assertEquals("date", "2014-08-26", resultSet.getString(4)); - assertEquals("timestamp", "Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(5)); - assertEquals("time", "03:25:45", resultSet.getString(6)); + assertEquals(2, resultSet.getInt(1), "int"); + assertEquals("hello", resultSet.getString(2), "string"); + assertEquals(1.2, resultSet.getDouble(3), 0, "double"); + assertEquals("2014-08-26", resultSet.getString(4), "date"); + assertEquals("Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(5), "timestamp"); + assertEquals("03:25:45", resultSet.getString(6), "time"); } try (ResultSet resultSet = @@ -1524,12 +1531,12 @@ public void testBind() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("int", 3, resultSet.getInt(1)); - assertEquals("string", "hello", resultSet.getString(2)); - assertEquals("double", 1.2, resultSet.getDouble(3), 0); - assertEquals("date", "2014-08-26", resultSet.getString(4)); - assertEquals("timestamp", "Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(5)); - assertEquals("time", "03:25:45", resultSet.getString(6)); + assertEquals(3, resultSet.getInt(1), "int"); + assertEquals("hello", resultSet.getString(2), "string"); + assertEquals(1.2, resultSet.getDouble(3), 0, "double"); + assertEquals("2014-08-26", resultSet.getString(4), "date"); + assertEquals("Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(5), "timestamp"); + assertEquals("03:25:45", resultSet.getString(6), "time"); } // describe mode @@ -1620,10 +1627,10 @@ public void testBind() throws Throwable { updateCounts = preparedStatement.executeBatch(); // GS optimizes this into one insert execution - assertEquals("Number of update counts", 16, updateCounts.length); + assertEquals(16, updateCounts.length, "Number of update counts"); for (int idx = 0; idx < 16; idx++) { - assertEquals("update count", 1, updateCounts[idx]); + assertEquals(1, updateCounts[idx], "update count"); } } } @@ -1798,7 +1805,7 @@ public void testBindTimestampNTZ() throws Throwable { int updateCount = preparedStatement.executeUpdate(); // update count should be 1 - assertEquals("update count", 1, updateCount); + assertEquals(1, updateCount, "update count"); // test the inserted rows try (ResultSet resultSet = @@ -1806,7 +1813,7 @@ public void testBindTimestampNTZ() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("timestamp", "Tue, 26 Aug 2014 03:52:00 Z", resultSet.getString(1)); + assertEquals("Tue, 26 Aug 2014 03:52:00 Z", resultSet.getString(1), "timestamp"); regularStatement.executeUpdate("truncate table testBindTimestampNTZ"); @@ -1816,7 +1823,7 @@ public void testBindTimestampNTZ() throws Throwable { updateCount = preparedStatement.executeUpdate(); // update count should be 1 - assertEquals("update count", 1, updateCount); + assertEquals(1, updateCount, "update count"); } // test the inserted rows try (ResultSet resultSet = @@ -1852,11 +1859,11 @@ public void testNullBind() throws Throwable { int[] updateCounts = preparedStatement.executeBatch(); // GS optimizes this into one insert execution - assertEquals("Number of update counts", 2, updateCounts.length); + assertEquals(2, updateCounts.length, "Number of update counts"); // update count should be 1 - assertEquals("update count", 1, updateCounts[0]); - assertEquals("update count", 1, updateCounts[1]); + assertEquals(1, updateCounts[0], "update count"); + assertEquals(1, updateCounts[1], "update count"); preparedStatement.clearBatch(); @@ -1869,11 +1876,11 @@ public void testNullBind() throws Throwable { updateCounts = preparedStatement.executeBatch(); // GS optimizes this into one insert execution - assertEquals("Number of update counts", 2, updateCounts.length); + assertEquals(2, updateCounts.length, "Number of update counts"); // update count should be 1 - assertEquals("update count", 1, updateCounts[0]); - assertEquals("update count", 1, updateCounts[1]); + assertEquals(1, updateCounts[0], "update count"); + assertEquals(1, updateCounts[1], "update count"); preparedStatement.clearBatch(); @@ -1883,10 +1890,10 @@ public void testNullBind() throws Throwable { updateCounts = preparedStatement.executeBatch(); // GS optimizes this into one insert execution - assertEquals("Number of update counts", 1, updateCounts.length); + assertEquals(1, updateCounts.length, "Number of update counts"); // update count should be 1 - assertEquals("update count", 1, updateCounts[0]); + assertEquals(1, updateCounts[0], "update count"); preparedStatement.clearBatch(); @@ -1956,12 +1963,12 @@ public void testSnow12603() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("integer", 1, resultSet.getInt(1)); - assertEquals("string", "hello", resultSet.getString(2)); - assertEquals("decimal", new BigDecimal("1.3"), resultSet.getBigDecimal(3)); - assertEquals("double", 1.3, resultSet.getDouble(4), 0); - assertEquals("date", "2014-08-26", resultSet.getString(5)); - assertEquals("timestamp", "Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(6)); + assertEquals(1, resultSet.getInt(1), "integer"); + assertEquals("hello", resultSet.getString(2), "string"); + assertEquals(new BigDecimal("1.3"), resultSet.getBigDecimal(3), "decimal"); + assertEquals(1.3, resultSet.getDouble(4), 0, "double"); + assertEquals("2014-08-26", resultSet.getString(5), "date"); + assertEquals("Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(6), "timestamp"); preparedStatement.setObject(1, 1, Types.INTEGER); preparedStatement.setObject(2, "hello", Types.VARCHAR); @@ -1986,12 +1993,12 @@ public void testSnow12603() throws Throwable { // assert we get 1 rows assertTrue(resultSet.next()); - assertEquals("integer", 1, resultSet.getInt(1)); - assertEquals("string", "hello", resultSet.getString(2)); - assertEquals("decimal", new BigDecimal("1.3"), resultSet.getBigDecimal(3)); - assertEquals("double", 1.3, resultSet.getDouble(4), 0); - assertEquals("date", "2014-08-26", resultSet.getString(5)); - assertEquals("timestamp", "Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(6)); + assertEquals(1, resultSet.getInt(1), "integer"); + assertEquals("hello", resultSet.getString(2), "string"); + assertEquals(new BigDecimal("1.3"), resultSet.getBigDecimal(3), "decimal"); + assertEquals(1.3, resultSet.getDouble(4), 0, "double"); + assertEquals("2014-08-26", resultSet.getString(5), "date"); + assertEquals("Mon, 25 Aug 2014 20:52:00 -0700", resultSet.getString(6), "timestamp"); } } } @@ -2016,11 +2023,11 @@ public void testSnow6290() throws Throwable { ResultSet res = statement.executeQuery("select ts from testSnow6290"); - assertTrue("expect a row", res.next()); + assertTrue(res.next(), "expect a row"); Timestamp tsFromDB = res.getTimestamp(1); - assertEquals("timestamp mismatch", ts.getTime(), tsFromDB.getTime()); + assertEquals(ts.getTime(), tsFromDB.getTime(), "timestamp mismatch"); } finally { statement.execute("DROP TABLE if exists testSnow6290"); } @@ -2056,28 +2063,28 @@ public void testGetObject() throws Throwable { resultSetMetaData = resultSet.getMetaData(); assertEquals( - "column class name=BigDecimal", Long.class.getName(), - resultSetMetaData.getColumnClassName(1)); + resultSetMetaData.getColumnClassName(1), + "column class name=BigDecimal"); // assert we get 1 rows assertTrue(resultSet.next()); - assertTrue("integer", resultSet.getObject(1) instanceof Long); + assertTrue(resultSet.getObject(1) instanceof Long, "integer"); } preparedStatement.setString(1, "hello"); try (ResultSet resultSet = preparedStatement.executeQuery()) { resultSetMetaData = resultSet.getMetaData(); assertEquals( - "column class name=String", String.class.getName(), - resultSetMetaData.getColumnClassName(1)); + resultSetMetaData.getColumnClassName(1), + "column class name=String"); // assert we get 1 rows assertTrue(resultSet.next()); - assertTrue("string", resultSet.getObject(1) instanceof String); + assertTrue(resultSet.getObject(1) instanceof String, "string"); } preparedStatement.setDouble(1, 1.2); @@ -2086,14 +2093,14 @@ public void testGetObject() throws Throwable { resultSetMetaData = resultSet.getMetaData(); assertEquals( - "column class name=Double", Double.class.getName(), - resultSetMetaData.getColumnClassName(1)); + resultSetMetaData.getColumnClassName(1), + "column class name=Double"); // assert we get 1 rows assertTrue(resultSet.next()); - assertTrue("double", resultSet.getObject(1) instanceof Double); + assertTrue(resultSet.getObject(1) instanceof Double, "double"); } preparedStatement.setTimestamp(1, new Timestamp(0)); @@ -2102,14 +2109,14 @@ public void testGetObject() throws Throwable { resultSetMetaData = resultSet.getMetaData(); assertEquals( - "column class name=Timestamp", Timestamp.class.getName(), - resultSetMetaData.getColumnClassName(1)); + resultSetMetaData.getColumnClassName(1), + "column class name=Timestamp"); // assert we get 1 rows assertTrue(resultSet.next()); - assertTrue("timestamp", resultSet.getObject(1) instanceof Timestamp); + assertTrue(resultSet.getObject(1) instanceof Timestamp, "timestamp"); } preparedStatement.setDate(1, new java.sql.Date(0)); @@ -2117,14 +2124,14 @@ public void testGetObject() throws Throwable { resultSetMetaData = resultSet.getMetaData(); assertEquals( - "column class name=Date", - java.sql.Date.class.getName(), - resultSetMetaData.getColumnClassName(1)); + Date.class.getName(), + resultSetMetaData.getColumnClassName(1), + "column class name=Date"); // assert we get 1 rows assertTrue(resultSet.next()); - assertTrue("date", resultSet.getObject(1) instanceof java.sql.Date); + assertTrue(resultSet.getObject(1) instanceof Date, "date"); } } } @@ -2135,7 +2142,7 @@ public void testGetDoubleForNull() throws Throwable { Statement stmt = connection.createStatement(); ResultSet resultSet = stmt.executeQuery("select cast(null as int) as null_int")) { assertTrue(resultSet.next()); - assertEquals("0 for null", 0, resultSet.getDouble(1), 0.0001); + assertEquals(0, resultSet.getDouble(1), 0.0001, "0 for null"); } } @@ -2182,7 +2189,7 @@ public void testPutViaExecuteQuery() throws Throwable { } } - @Ignore("takes 7 min. enable this for long running tests") + @Disabled("takes 7 min. enable this for long running tests") @Test public void testSnow16332() throws Throwable { // use v1 query request API and inject 200ms socket timeout for first @@ -2292,7 +2299,7 @@ public void run() { fail("should be canceled"); } catch (SQLException ex) { // assert the sqlstate is what we expect (QUERY CANCELLED) - assertEquals("sqlstate mismatch", SqlState.QUERY_CANCELED, ex.getSQLState()); + assertEquals(SqlState.QUERY_CANCELED, ex.getSQLState(), "sqlstate mismatch"); } } } @@ -2329,7 +2336,7 @@ public void testSnow14774() throws Throwable { tsStrInLA = sdf.format(tsInLA); // the timestamp in LA and in UTC should be the same - assertEquals("timestamp values not equal", tsStrInUTC, tsStrInLA); + assertEquals(tsStrInUTC, tsStrInLA, "timestamp values not equal"); } // 30 minutes before daylight saving change try (ResultSet res = statement.executeQuery("select '2015-03-08 01:30:00'::timestamp_ntz")) { @@ -2351,7 +2358,7 @@ public void testSnow14774() throws Throwable { tsStrInLA = sdf.format(tsInLA); // the timestamp in LA and in UTC should be the same - assertEquals("timestamp values not equal", tsStrInUTC, tsStrInLA); + assertEquals(tsStrInUTC, tsStrInLA, "timestamp values not equal"); } } } @@ -2416,7 +2423,7 @@ public void testSnow19819() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnTestaccount.class) + @DontRunOnTestaccount public void testClientInfo() throws Throwable { System.setProperty( "snowflake.client.info", @@ -2425,22 +2432,22 @@ public void testClientInfo() throws Throwable { Statement statement = connection.createStatement(); ResultSet res = statement.executeQuery("select current_session_client_info()")) { - assertTrue("result expected", res.next()); + assertTrue(res.next(), "result expected"); String clientInfoJSONStr = res.getString(1); JsonNode clientInfoJSON = mapper.readTree(clientInfoJSONStr); // assert that spark version and spark app are found - assertEquals("spark version mismatch", "3.0.0", clientInfoJSON.get("spark.version").asText()); + assertEquals("3.0.0", clientInfoJSON.get("spark.version").asText(), "spark version mismatch"); assertEquals( - "snowflakedb version mismatch", "2.8.5", - clientInfoJSON.get("spark.snowflakedb.version").asText()); + clientInfoJSON.get("spark.snowflakedb.version").asText(), + "snowflakedb version mismatch"); assertEquals( - "spark app mismatch", "SnowflakeSourceSuite", - clientInfoJSON.get("spark.app.name").asText()); + clientInfoJSON.get("spark.app.name").asText(), + "spark app mismatch"); closeSQLObjects(res, statement, connection); } @@ -2466,7 +2473,7 @@ public void testLargeResultSet() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testSnow26503() throws Throwable { ResultSetMetaData resultSetMetaData; String queryId = null; @@ -2630,7 +2637,7 @@ public void testSnow31104() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutGet() throws Throwable { List accounts = Arrays.asList(null, "s3testaccount", "azureaccount", "gcpaccount"); @@ -2640,7 +2647,8 @@ public void testPutGet() throws Throwable { try { String sourceFilePath = getFullPathFileInResource(TEST_DATA_FILE); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; @@ -2648,16 +2656,16 @@ public void testPutGet() throws Throwable { statement.execute("CREATE OR REPLACE STAGE testPutGet_stage"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePath + " @testPutGet_stage")); + statement.execute("PUT file://" + sourceFilePath + " @testPutGet_stage"), + "Failed to put a file"); findFile(statement, "ls @testPutGet_stage/"); // download the file we just uploaded to stage assertTrue( - "Failed to get a file", statement.execute( - "GET @testPutGet_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @testPutGet_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get a file"); // Make sure that the downloaded file exists, it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + TEST_DATA_FILE + ".gz"); @@ -2685,7 +2693,7 @@ public void testPutGet() throws Throwable { * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutGetToUnencryptedStage() throws Throwable { List accounts = Arrays.asList(null, "s3testaccount", "azureaccount", "gcpaccount"); @@ -2695,7 +2703,8 @@ public void testPutGetToUnencryptedStage() throws Throwable { try { String sourceFilePath = getFullPathFileInResource(TEST_DATA_FILE); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; @@ -2705,18 +2714,16 @@ public void testPutGetToUnencryptedStage() throws Throwable { "CREATE OR REPLACE STAGE testPutGet_unencstage encryption=(TYPE='SNOWFLAKE_SSE')"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePath + " @testPutGet_unencstage")); + statement.execute("PUT file://" + sourceFilePath + " @testPutGet_unencstage"), + "Failed to put a file"); findFile(statement, "ls @testPutGet_unencstage/"); // download the file we just uploaded to stage assertTrue( - "Failed to get a file", statement.execute( - "GET @testPutGet_unencstage 'file://" - + destFolderCanonicalPath - + "' parallel=8")); + "GET @testPutGet_unencstage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get a file"); // Make sure that the downloaded file exists, it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + TEST_DATA_FILE + ".gz"); @@ -2738,15 +2745,15 @@ public void testPutGetToUnencryptedStage() throws Throwable { } /** Prepare statement will fail if the connection is already closed. */ - @Test(expected = SQLException.class) - public void testNotClosedSession() throws Throwable { + @Test + public void testNotClosedSession() throws SQLException { Connection connection = getConnection(); connection.close(); - connection.prepareStatement("select 1"); + assertThrows(SnowflakeSQLException.class, () -> connection.prepareStatement("select 1")); } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testToTimestampNullBind() throws Throwable { try (Connection connection = getConnection(); PreparedStatement preparedStatement = diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverLatestIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverLatestIT.java index 91052fd7c..fde744f15 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverLatestIT.java @@ -7,10 +7,11 @@ import static net.snowflake.client.jdbc.SnowflakeDriver.implementVersion; import static net.snowflake.client.jdbc.SnowflakeDriverIT.findFile; import static net.snowflake.client.jdbc.SnowflakeResultSetSerializableV1.mapper; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.fasterxml.jackson.databind.JsonNode; import com.google.cloud.storage.StorageException; @@ -39,11 +40,10 @@ import java.util.Properties; import java.util.UUID; import java.util.zip.GZIPInputStream; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.RunningOnTestaccount; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.annotations.DontRunOnTestaccount; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.Constants; import net.snowflake.client.core.OCSPMode; import net.snowflake.client.core.SFSession; @@ -58,11 +58,10 @@ import net.snowflake.common.core.SqlState; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** * General JDBC tests for the latest JDBC driver. This doesn't work for the oldest supported driver. @@ -70,10 +69,10 @@ * is not applicable. If it is applicable, move tests to SnowflakeDriverIT so that both the latest * and oldest supported driver run the tests. */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class SnowflakeDriverLatestIT extends BaseJDBCTest { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); - @Rule public TemporaryFolder tmpFolder2 = new TemporaryFolder(); + @TempDir private File tmpFolder; + @TempDir private File tmpFolder2; public String testStageName = String.format("test_stage_%s", UUID.randomUUID().toString()).replaceAll("-", "_"); @@ -105,7 +104,7 @@ public void testStaticVersionMatchesManifest() { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnTestaccount.class) + @DontRunOnTestaccount public void testClientInfoConnectionProperty() throws Throwable { String clientInfoJSONStr = null; JsonNode clientInfoJSON = null; @@ -122,11 +121,11 @@ public void testClientInfoConnectionProperty() throws Throwable { clientInfoJSONStr = res.getString(1); clientInfoJSON = mapper.readTree(clientInfoJSONStr); // assert that spart version and spark app are found - assertEquals("spark version mismatch", "3.0.0", clientInfoJSON.get("spark.version").asText()); + assertEquals("3.0.0", clientInfoJSON.get("spark.version").asText(), "spark version mismatch"); assertEquals( - "spark app mismatch", "SnowflakeSourceSuite", - clientInfoJSON.get("spark.app.name").asText()); + clientInfoJSON.get("spark.app.name").asText(), + "spark app mismatch"); } // Test that when session property is set, connection parameter overrides it @@ -142,11 +141,11 @@ public void testClientInfoConnectionProperty() throws Throwable { clientInfoJSONStr = res.getString(1); clientInfoJSON = mapper.readTree(clientInfoJSONStr); // assert that spart version and spark app are found - assertEquals("spark version mismatch", "3.0.0", clientInfoJSON.get("spark.version").asText()); + assertEquals("3.0.0", clientInfoJSON.get("spark.version").asText(), "spark version mismatch"); assertEquals( - "spark app mismatch", "SnowflakeSourceSuite", - clientInfoJSON.get("spark.app.name").asText()); + clientInfoJSON.get("spark.app.name").asText(), + "spark app mismatch"); } System.clearProperty("snowflake.client.info"); } @@ -163,7 +162,7 @@ public void testGetSessionID() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutThreshold() throws SQLException { try (Connection connection = getConnection()) { // assert that threshold equals default 200 from server side @@ -202,9 +201,10 @@ public void testPutThreshold() throws SQLException { /** Test API for Spark connector for FileTransferMetadata */ @Test - @Ignore + @Disabled public void testGCPFileTransferMetadataWithOneFile() throws Throwable { - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); try (Connection connection = getConnection("gcpaccount"); @@ -266,9 +266,9 @@ public void testGCPFileTransferMetadataWithOneFile() throws Throwable { // Download two files and verify their content. assertTrue( - "Failed to get files", statement.execute( - "GET @" + testStageName + " 'file://" + destFolderCanonicalPath + "/' parallel=8")); + "GET @" + testStageName + " 'file://" + destFolderCanonicalPath + "/' parallel=8"), + "Failed to get files"); // Make sure that the downloaded files are EQUAL, // they should be gzip compressed @@ -284,9 +284,10 @@ public void testGCPFileTransferMetadataWithOneFile() throws Throwable { /** Test API for Kafka connector for FileTransferMetadata */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testAzureS3FileTransferMetadataWithOneFile() throws Throwable { - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); List supportedAccounts = Arrays.asList("s3testaccount", "azureaccount"); @@ -353,13 +354,13 @@ public void testAzureS3FileTransferMetadataWithOneFile() throws Throwable { // Download two files and verify their content. assertTrue( - "Failed to get files", statement.execute( "GET @" + testStageName + " 'file://" + destFolderCanonicalPath - + "/' parallel=8")); + + "/' parallel=8"), + "Failed to get files"); // Make sure that the downloaded files are EQUAL, // they should be gzip compressed @@ -376,7 +377,7 @@ public void testAzureS3FileTransferMetadataWithOneFile() throws Throwable { /** Negative test for FileTransferMetadata. It is only supported for PUT. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGCPFileTransferMetadataNegativeOnlySupportPut() throws Throwable { int expectExceptionCount = 1; int actualExceptionCount = -1; @@ -392,7 +393,8 @@ public void testGCPFileTransferMetadataNegativeOnlySupportPut() throws Throwable SFSession sfSession = connection.unwrap(SnowflakeConnectionV1.class).getSfSession(); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String getCommand = "get @" + testStageName + " file://" + destFolderCanonicalPath; @@ -487,23 +489,26 @@ public void testGetPropertyInfo() throws SQLException { * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutOverwriteFalseNoDigest() throws Throwable { // create 2 files: an original, and one that will overwrite the original - File file1 = tmpFolder.newFile("testfile.csv"); + File file1 = new File(tmpFolder, "testfile.csv"); + file1.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(file1))) { bw.write("Writing original file content. This should get overwritten."); } - File file2 = tmpFolder2.newFile("testfile.csv"); + File file2 = new File(tmpFolder2, "testfile.csv"); + file2.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(file2))) { bw.write("This is all new! This should be the result of the overwriting."); } String sourceFilePathOriginal = file1.getCanonicalPath(); String sourceFilePathOverwrite = file2.getCanonicalPath(); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; @@ -518,25 +523,25 @@ public void testPutOverwriteFalseNoDigest() throws Throwable { // create a stage to put the file in statement.execute("CREATE OR REPLACE STAGE testing_stage"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePathOriginal + " @testing_stage")); + statement.execute("PUT file://" + sourceFilePathOriginal + " @testing_stage"), + "Failed to put a file"); // check that file exists in stage after PUT findFile(statement, "ls @testing_stage/"); // put another file in same stage with same filename with overwrite = true assertTrue( - "Failed to put a file", statement.execute( - "PUT file://" + sourceFilePathOverwrite + " @testing_stage overwrite=false")); + "PUT file://" + sourceFilePathOverwrite + " @testing_stage overwrite=false"), + "Failed to put a file"); // check that file exists in stage after PUT findFile(statement, "ls @testing_stage/"); // get file from new stage assertTrue( - "Failed to get files", statement.execute( - "GET @testing_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @testing_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get files"); // Make sure that the downloaded file exists; it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + "testfile.csv.gz"); @@ -564,11 +569,12 @@ public void testPutOverwriteFalseNoDigest() throws Throwable { * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutDisable() throws Throwable { // create a file - File file = tmpFolder.newFile("testfile99.csv"); + File file = new File(tmpFolder, "testfile99.csv"); + file.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) { bw.write("This content won't be uploaded as PUT is disabled."); } @@ -584,7 +590,7 @@ public void testPutDisable() throws Throwable { Statement statement = connection.createStatement()) { statement.execute("PUT file://" + sourceFilePathOriginal + " @testPutGet_disable_stage"); - assertTrue("Shouldn't come here", false); + assertTrue(false, "Shouldn't come here"); } catch (Exception ex) { // Expected assertTrue(ex.getMessage().equalsIgnoreCase("File transfers have been disabled.")); @@ -598,11 +604,12 @@ public void testPutDisable() throws Throwable { * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGetDisable() throws Throwable { // create a folder - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); Properties paramProperties = new Properties(); @@ -616,7 +623,7 @@ public void testGetDisable() throws Throwable { statement.execute( "GET @testPutGet_disable_stage 'file://" + destFolderCanonicalPath + "' parallel=8"); - assertTrue("Shouldn't come here", false); + assertTrue(false, "Shouldn't come here"); } catch (Exception ex) { // Expected assertTrue(ex.getMessage().equalsIgnoreCase("File transfers have been disabled.")); @@ -798,7 +805,7 @@ public void testSnow76376() throws Throwable { * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGeoOutputTypes() throws Throwable { Properties paramProperties = new Properties(); @@ -862,7 +869,7 @@ private void testGeoOutputTypeSingle( } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGeoMetadata() throws Throwable { Properties paramProperties = new Properties(); @@ -913,7 +920,7 @@ private void testGeoMetadataSingle( } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGeometryOutputTypes() throws Throwable { Properties paramProperties = new Properties(); @@ -967,7 +974,7 @@ private void testGeometryOutputTypeSingle( } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testGeometryMetadata() throws Throwable { Properties paramProperties = new Properties(); @@ -1015,7 +1022,7 @@ private void testGeometryMetadataSingle( * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutGetGcsDownscopedCredential() throws Throwable { Properties paramProperties = new Properties(); paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", true); @@ -1027,7 +1034,7 @@ public void testPutGetGcsDownscopedCredential() throws Throwable { /** Added in > 3.15.0 */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutGetGcsDownscopedCredentialWithDisabledDefaultCredentials() throws Throwable { Properties paramProperties = new Properties(); paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", true); @@ -1041,7 +1048,8 @@ public void testPutGetGcsDownscopedCredentialWithDisabledDefaultCredentials() th private void putAndGetFile(Statement statement) throws Throwable { String sourceFilePath = getFullPathFileInResource(TEST_DATA_FILE_2); - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; @@ -1049,16 +1057,16 @@ private void putAndGetFile(Statement statement) throws Throwable { statement.execute("CREATE OR REPLACE STAGE testPutGet_stage"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePath + " @testPutGet_stage")); + statement.execute("PUT file://" + sourceFilePath + " @testPutGet_stage"), + "Failed to put a file"); findFile(statement, "ls @testPutGet_stage/"); // download the file we just uploaded to stage assertTrue( - "Failed to get a file", statement.execute( - "GET @testPutGet_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @testPutGet_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get a file"); // Make sure that the downloaded file exists, it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + TEST_DATA_FILE_2 + ".gz"); @@ -1088,25 +1096,28 @@ private void putAndGetFile(Statement statement) throws Throwable { * @throws Throwable */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutGetLargeFileGCSDownscopedCredential() throws Throwable { Properties paramProperties = new Properties(); paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", true); try (Connection connection = getConnection("gcpaccount", paramProperties); Statement statement = connection.createStatement()) { try { - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; - File largeTempFile = tmpFolder.newFile("largeFile.csv"); + File largeTempFile = new File(tmpFolder, "largeFile.csv"); + largeTempFile.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(largeTempFile))) { bw.write("Creating large test file for GCP PUT/GET test"); bw.write(System.lineSeparator()); bw.write("Creating large test file for GCP PUT/GET test"); bw.write(System.lineSeparator()); } - File largeTempFile2 = tmpFolder.newFile("largeFile2.csv"); + File largeTempFile2 = new File(tmpFolder, "largeFile2.csv"); + largeTempFile2.createNewFile(); String sourceFilePath = largeTempFile.getCanonicalPath(); @@ -1120,8 +1131,8 @@ public void testPutGetLargeFileGCSDownscopedCredential() throws Throwable { // create a stage to put the file in statement.execute("CREATE OR REPLACE STAGE largefile_stage"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePath + " @largefile_stage")); + statement.execute("PUT file://" + sourceFilePath + " @largefile_stage"), + "Failed to put a file"); // check that file exists in stage after PUT findFile(statement, "ls @largefile_stage/"); @@ -1136,9 +1147,9 @@ public void testPutGetLargeFileGCSDownscopedCredential() throws Throwable { // get file from new stage assertTrue( - "Failed to get files", statement.execute( - "GET @extra_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @extra_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get files"); // Make sure that the downloaded file exists; it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + "bigFile.csv.gz"); @@ -1165,24 +1176,27 @@ public void testPutGetLargeFileGCSDownscopedCredential() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutGetLargeFileAzure() throws Throwable { Properties paramProperties = new Properties(); try (Connection connection = getConnection("azureaccount", paramProperties); Statement statement = connection.createStatement()) { try { - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String destFolderCanonicalPathWithSeparator = destFolderCanonicalPath + File.separator; - File largeTempFile = tmpFolder.newFile("largeFile.csv"); + File largeTempFile = new File(tmpFolder, "largeFile.csv"); + largeTempFile.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(largeTempFile))) { bw.write("Creating large test file for Azure PUT/GET test"); bw.write(System.lineSeparator()); bw.write("Creating large test file for Azure PUT/GET test"); bw.write(System.lineSeparator()); } - File largeTempFile2 = tmpFolder.newFile("largeFile2.csv"); + File largeTempFile2 = new File(tmpFolder, "largeFile2.csv"); + largeTempFile2.createNewFile(); String sourceFilePath = largeTempFile.getCanonicalPath(); @@ -1196,8 +1210,8 @@ public void testPutGetLargeFileAzure() throws Throwable { // create a stage to put the file in statement.execute("CREATE OR REPLACE STAGE largefile_stage"); assertTrue( - "Failed to put a file", - statement.execute("PUT file://" + sourceFilePath + " @largefile_stage")); + statement.execute("PUT file://" + sourceFilePath + " @largefile_stage"), + "Failed to put a file"); // check that file exists in stage after PUT findFile(statement, "ls @largefile_stage/"); @@ -1212,9 +1226,9 @@ public void testPutGetLargeFileAzure() throws Throwable { // get file from new stage assertTrue( - "Failed to get files", statement.execute( - "GET @extra_stage 'file://" + destFolderCanonicalPath + "' parallel=8")); + "GET @extra_stage 'file://" + destFolderCanonicalPath + "' parallel=8"), + "Failed to get files"); // Make sure that the downloaded file exists; it should be gzip compressed File downloaded = new File(destFolderCanonicalPathWithSeparator + "bigFile.csv.gz"); @@ -1259,9 +1273,10 @@ private void copyContentFrom(File file1, File file2) throws Exception { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutS3RegionalUrl() throws Throwable { - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); List supportedAccounts = Arrays.asList("s3testaccount", "azureaccount"); @@ -1344,13 +1359,13 @@ public void testPutS3RegionalUrl() throws Throwable { // Download two files and verify their content. assertTrue( - "Failed to get files", statement.execute( "GET @" + testStageName + " 'file://" + destFolderCanonicalPath - + "/' parallel=8")); + + "/' parallel=8"), + "Failed to get files"); // Make sure that the downloaded files are EQUAL, // they should be gzip compressed @@ -1370,7 +1385,7 @@ public void testPutS3RegionalUrl() throws Throwable { * and Azure */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testAzureS3UploadStreamingIngestFileMetadata() throws Throwable { String clientName = "clientName"; String clientKey = "clientKey"; @@ -1433,7 +1448,8 @@ public void testAzureS3UploadStreamingIngestFileMetadata() throws Throwable { } } - @Test(expected = SnowflakeSQLException.class) + @Test + @DontRunOnGithubActions public void testNoSpaceLeftOnDeviceException() throws SQLException { List supportedAccounts = Arrays.asList("gcpaccount", "s3testaccount", "azureaccount"); for (String accountName : supportedAccounts) { @@ -1452,16 +1468,19 @@ public void testNoSpaceLeftOnDeviceException() throws SQLException { SnowflakeStorageClient client = StorageClientFactory.getFactory().createClient(info, 1, null, /* session= */ null); - client.handleStorageException( - new StorageException( - client.getMaxRetries(), - Constants.NO_SPACE_LEFT_ON_DEVICE_ERR, - new IOException(Constants.NO_SPACE_LEFT_ON_DEVICE_ERR)), - client.getMaxRetries(), - "download", - null, - command, - null); + assertThrows( + SnowflakeSQLException.class, + () -> + client.handleStorageException( + new StorageException( + client.getMaxRetries(), + Constants.NO_SPACE_LEFT_ON_DEVICE_ERR, + new IOException(Constants.NO_SPACE_LEFT_ON_DEVICE_ERR)), + client.getMaxRetries(), + "download", + null, + command, + null)); } finally { statement.execute("DROP STAGE if exists testPutGet_stage"); } @@ -1471,14 +1490,63 @@ public void testNoSpaceLeftOnDeviceException() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @Disabled // TODO: ignored until SNOW-1616480 is resolved + public void testUploadWithGCSPresignedUrlWithoutConnection() throws Throwable { + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); + String destFolderCanonicalPath = destFolder.getCanonicalPath(); + // set parameter for presignedUrl upload instead of downscoped token + Properties paramProperties = new Properties(); + paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", false); + try (Connection connection = getConnection("gcpaccount", paramProperties); + Statement statement = connection.createStatement()) { + try { + // create a stage to put the file in + statement.execute("CREATE OR REPLACE STAGE " + testStageName); + + SFSession sfSession = connection.unwrap(SnowflakeConnectionV1.class).getSfSession(); + + // Test put file with internal compression + String putCommand = "put file:///dummy/path/file1.gz @" + testStageName; + SnowflakeFileTransferAgent sfAgent = + new SnowflakeFileTransferAgent(putCommand, sfSession, new SFStatement(sfSession)); + List metadata = sfAgent.getFileTransferMetadatas(); + + String srcPath = getFullPathFileInResource(TEST_DATA_FILE); + for (SnowflakeFileTransferMetadata oneMetadata : metadata) { + InputStream inputStream = new FileInputStream(srcPath); + + assertTrue(oneMetadata.isForOneFile()); + SnowflakeFileTransferAgent.uploadWithoutConnection( + SnowflakeFileTransferConfig.Builder.newInstance() + .setSnowflakeFileTransferMetadata(oneMetadata) + .setUploadStream(inputStream) + .setRequireCompress(true) + .setNetworkTimeoutInMilli(0) + .setOcspMode(OCSPMode.FAIL_OPEN) + .build()); + } + + assertTrue( + statement.execute( + "GET @" + testStageName + " 'file://" + destFolderCanonicalPath + "/' parallel=8"), + "Failed to get files"); + assertTrue(isFileContentEqual(srcPath, false, destFolderCanonicalPath + "/file1.gz", true)); + } finally { + statement.execute("DROP STAGE if exists " + testStageName); + } + } + } + + @Test + @DontRunOnGithubActions public void testUploadWithGCSDownscopedCredentialWithoutConnection() throws Throwable { uploadWithGCSDownscopedCredentialWithoutConnection(); } /** Added in > 3.15.0 */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testUploadWithGCSDownscopedCredentialAndDisabledGcsDefaultCredentialsWithoutConnection() throws Throwable { @@ -1491,7 +1559,8 @@ public void testUploadWithGCSDownscopedCredentialWithoutConnection() throws Thro } private void uploadWithGCSDownscopedCredentialWithoutConnection() throws Throwable { - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); Properties paramProperties = new Properties(); paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", true); @@ -1529,9 +1598,9 @@ private void uploadWithGCSDownscopedCredentialWithoutConnection() throws Throwab .setOcspMode(OCSPMode.FAIL_OPEN) .build()); assertTrue( - "Failed to get files with down-scoped token", statement.execute( - "GET @" + testStageName + " 'file://" + destFolderCanonicalPath + "/'")); + "GET @" + testStageName + " 'file://" + destFolderCanonicalPath + "/'"), + "Failed to get files with down-scoped token"); assertTrue( isFileContentEqual( srcPath, false, destFolderCanonicalPath + "/" + targetFileName, true)); @@ -1551,7 +1620,7 @@ private void uploadWithGCSDownscopedCredentialWithoutConnection() throws Throwab * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testHTAPOptimizations() throws SQLException { try { // Set the HTAP test parameter to true @@ -1623,7 +1692,7 @@ public void testHTAPOptimizations() throws SQLException { * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testHTAPStatementParameterCaching() throws SQLException { // Set the HTAP test parameter to true try (Connection con = getSnowflakeAdminConnection()) { @@ -1682,9 +1751,10 @@ public void testHTAPStatementParameterCaching() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testS3PutInGS() throws Throwable { - File destFolder = tmpFolder.newFolder(); + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); Properties paramProperties = new Properties(); try (Connection connection = getConnection("s3testaccount", paramProperties); @@ -1711,7 +1781,7 @@ public void testS3PutInGS() throws Throwable { new FileInputStream(destFolderCanonicalPath + "/" + fileName); String downloadedFile = IOUtils.toString(downloadedFileStream, StandardCharsets.UTF_8); assertTrue( - "downloaded content does not equal uploaded content", content.equals(downloadedFile)); + content.equals(downloadedFile), "downloaded content does not equal uploaded content"); } finally { statement.execute("DROP STAGE if exists " + testStageName); } diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverTest.java index 8b42be5c4..f268577e1 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeDriverTest.java @@ -3,13 +3,13 @@ */ package net.snowflake.client.jdbc; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.SQLException; import java.util.ArrayList; @@ -19,7 +19,7 @@ import java.util.Locale; import java.util.Map; import java.util.Properties; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Driver unit test */ public class SnowflakeDriverTest { @@ -52,16 +52,16 @@ void match(String url, SnowflakeConnectString sc) { int port = sc.getPort(); Map parameters = sc.getParameters(); - assertEquals("URL scheme: " + url, this.scheme, scheme); - assertEquals("URL scheme: " + url, this.host, host); - assertEquals("URL scheme: " + url, this.port, port); - assertEquals("URL scheme: " + url, this.parameters.size(), parameters.size()); - assertEquals("URL scheme. " + url, this.account, account); + assertEquals(this.scheme, scheme, "URL scheme: " + url); + assertEquals(this.host, host, "URL scheme: " + url); + assertEquals(this.port, port, "URL scheme: " + url); + assertEquals(this.parameters.size(), parameters.size(), "URL scheme: " + url); + assertEquals(this.account, account, "URL scheme. " + url); for (Map.Entry entry : this.parameters.entrySet()) { String k = entry.getKey().toUpperCase(Locale.US); Object v = parameters.get(k); - assertEquals("URL scheme: " + url + ", key: " + k, entry.getValue(), v); + assertEquals(entry.getValue(), v, "URL scheme: " + url + ", key: " + k); } } } @@ -355,7 +355,7 @@ public void testAcceptUrls() throws Exception { expectedParameters)); for (TestCase t : testCases) { - assertTrue("URL is not valid: " + t.url, snowflakeDriver.acceptsURL(t.url)); + assertTrue(snowflakeDriver.acceptsURL(t.url), "URL is not valid: " + t.url); t.match(t.url, SnowflakeConnectString.parse(t.url, SnowflakeDriver.EMPTY_PROPERTIES)); } diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeGcsClientHandleExceptionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeGcsClientHandleExceptionLatestIT.java index 22c26465d..fd6ee0d81 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeGcsClientHandleExceptionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeGcsClientHandleExceptionLatestIT.java @@ -1,5 +1,9 @@ package net.snowflake.client.jdbc; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + import com.google.cloud.storage.StorageException; import java.io.File; import java.io.IOException; @@ -10,26 +14,23 @@ import java.sql.Statement; import java.util.Properties; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.Constants; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.SFStatement; import net.snowflake.client.jdbc.cloud.storage.SnowflakeGCSClient; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.mockito.Mockito; /** Test for SnowflakeGcsClient handle exception function, only work with latest driver */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class SnowflakeGcsClientHandleExceptionLatestIT extends AbstractDriverIT { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; private Connection connection; private SFStatement sfStatement; private SFSession sfSession; @@ -38,7 +39,7 @@ public class SnowflakeGcsClientHandleExceptionLatestIT extends AbstractDriverIT private int overMaxRetry; private int maxRetry; - @Before + @BeforeEach public void setup() throws SQLException { Properties paramProperties = new Properties(); paramProperties.put("GCS_USE_DOWNSCOPED_CREDENTIAL", true); @@ -59,7 +60,7 @@ public void setup() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void error401RenewExpired() throws SQLException, InterruptedException { // Unauthenticated, renew is called. spyingClient.handleStorageException( @@ -94,94 +95,117 @@ public void run() { thread.start(); thread.interrupt(); thread.join(); - Assert.assertNull("Exception must not have been thrown in here", exceptionContainer[0]); + assertNull(exceptionContainer[0], "Exception must not have been thrown in here"); Mockito.verify(spyingClient, Mockito.times(2)).renew(Mockito.anyMap()); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void error401OverMaxRetryThrow() throws SQLException { - spyingClient.handleStorageException( - new StorageException(401, "Unauthenticated"), - overMaxRetry, - "upload", - sfSession, - command, - null); + @Test + @DontRunOnGithubActions + public void error401OverMaxRetryThrow() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new StorageException(401, "Unauthenticated"), + overMaxRetry, + "upload", + sfSession, + command, + null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorInvalidKey() throws SQLException { + @Test + @DontRunOnGithubActions + public void errorInvalidKey() { // Unauthenticated, renew is called. - spyingClient.handleStorageException( - new Exception(new InvalidKeyException()), 0, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new Exception(new InvalidKeyException()), 0, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @Test + @DontRunOnGithubActions public void errorInterruptedException() throws SQLException { // Can still retry, no error thrown try { spyingClient.handleStorageException( new InterruptedException(), 0, "upload", sfSession, command, null); } catch (Exception e) { - Assert.fail("Should not have exception here"); + fail("Should not have exception here"); } Mockito.verify(spyingClient, Mockito.never()).renew(Mockito.anyMap()); - spyingClient.handleStorageException( - new InterruptedException(), 26, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new InterruptedException(), 26, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorSocketTimeoutException() throws SQLException { + @Test + @DontRunOnGithubActions + public void errorSocketTimeoutException() throws SnowflakeSQLException { // Can still retry, no error thrown try { spyingClient.handleStorageException( new SocketTimeoutException(), 0, "upload", sfSession, command, null); } catch (Exception e) { - Assert.fail("Should not have exception here"); + fail("Should not have exception here"); } Mockito.verify(spyingClient, Mockito.never()).renew(Mockito.anyMap()); - spyingClient.handleStorageException( - new SocketTimeoutException(), 26, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new SocketTimeoutException(), 26, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorUnknownException() throws SQLException { + @Test + @DontRunOnGithubActions + public void errorUnknownException() { // Unauthenticated, renew is called. - spyingClient.handleStorageException(new Exception(), 0, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new Exception(), 0, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorWithNullSession() throws SQLException { - spyingClient.handleStorageException( - new StorageException(401, "Unauthenticated"), 0, "upload", null, command, null); + @Test + @DontRunOnGithubActions + public void errorWithNullSession() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new StorageException(401, "Unauthenticated"), 0, "upload", null, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorNoSpaceLeftOnDevice() throws SQLException, IOException { - File destFolder = tmpFolder.newFolder(); + @Test + @DontRunOnGithubActions + public void errorNoSpaceLeftOnDevice() throws IOException { + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String getCommand = "get @testPutGet_stage/" + TEST_DATA_FILE + " 'file://" + destFolderCanonicalPath + "'"; - spyingClient.handleStorageException( - new StorageException( - maxRetry, - Constants.NO_SPACE_LEFT_ON_DEVICE_ERR, - new IOException(Constants.NO_SPACE_LEFT_ON_DEVICE_ERR)), - 0, - "download", - null, - getCommand, - null); + assertThrows( + SQLException.class, + () -> + spyingClient.handleStorageException( + new StorageException( + maxRetry, + Constants.NO_SPACE_LEFT_ON_DEVICE_ERR, + new IOException(Constants.NO_SPACE_LEFT_ON_DEVICE_ERR)), + 0, + "download", + null, + getCommand, + null)); } - @After + @AfterEach public void cleanUp() throws SQLException { sfStatement.close(); connection.close(); diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableArrowIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableArrowIT.java deleted file mode 100644 index d9cb057d2..000000000 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableArrowIT.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.snowflake.client.jdbc; - -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; - -/** Test SnowflakeResultSetSerializable for Arrow */ -@Category(TestCategoryArrow.class) -public class SnowflakeResultSetSerializableArrowIT extends SnowflakeResultSetSerializableIT { - public SnowflakeResultSetSerializableArrowIT() { - super("arrow"); - } -} diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableIT.java index f9c2bb66d..3b6206f55 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeResultSetSerializableIT.java @@ -1,10 +1,10 @@ package net.snowflake.client.jdbc; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileInputStream; @@ -20,41 +20,33 @@ import java.util.List; import java.util.Properties; import javax.annotation.Nullable; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryResultSet; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; /** SnowflakeResultSetSerializable tests */ -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class SnowflakeResultSetSerializableIT extends BaseJDBCTest { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; private static boolean developPrint = false; - private String queryResultFormat; - // sfFullURL is used to support private link URL. // This test case is not for private link env, so just use a valid URL for testing purpose. private String sfFullURL = "https://sfctest0.snowflakecomputing.com"; - public SnowflakeResultSetSerializableIT() { - this("json"); - } - - SnowflakeResultSetSerializableIT(String format) { - queryResultFormat = format; + public Connection init(String queryResultFormat) throws SQLException { + return init(null, queryResultFormat); } - public Connection init() throws SQLException { - return init(null); - } - - public Connection init(@Nullable Properties properties) throws SQLException { + public Connection init(@Nullable Properties properties, String queryResultFormat) + throws SQLException { Connection conn = BaseJDBCTest.getConnection(properties); try (Statement stmt = conn.createStatement()) { stmt.execute("alter session set jdbc_query_result_format = '" + queryResultFormat + "'"); @@ -121,7 +113,7 @@ private List serializeResultSet( SnowflakeResultSetSerializable entry = resultSetChunks.get(i); // Write object to file - String tmpFileName = tmpFolder.getRoot().getPath() + "_result_" + i + "." + fileNameAppendix; + String tmpFileName = tmpFolder.getPath() + "_result_" + i + "." + fileNameAppendix; try (FileOutputStream fo = new FileOutputStream(tmpFileName); ObjectOutputStream so = new ObjectOutputStream(fo)) { so.writeObject(entry); @@ -238,11 +230,16 @@ private String deserializeResultSetWithProperties(List files, Properties * @throws Throwable If any error happens. */ private void testBasicTableHarness( - int rowCount, long maxSizeInBytes, String whereClause, boolean needSetupTable, boolean async) + int rowCount, + long maxSizeInBytes, + String whereClause, + boolean needSetupTable, + boolean async, + String queryResultFormat) throws Throwable { List fileNameList = null; String originalResultCSVString = null; - try (Connection connection = init()) { + try (Connection connection = init(queryResultFormat)) { Statement statement = connection.createStatement(); if (developPrint) { @@ -289,57 +286,61 @@ private void testBasicTableHarness( assertEquals(chunkResultString, originalResultCSVString); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testBasicTableWithEmptyResult() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testBasicTableWithEmptyResult(String queryResultFormat) throws Throwable { // Use complex WHERE clause in order to test both ARROW and JSON. // It looks GS only generates JSON format result. - testBasicTableHarness(10, 1024, "where int_c * int_c = 2", true, false); + testBasicTableHarness(10, 1024, "where int_c * int_c = 2", true, false, queryResultFormat); // Test Async mode - testBasicTableHarness(10, 1024, "where int_c * int_c = 2", true, true); + testBasicTableHarness(10, 1024, "where int_c * int_c = 2", true, true, queryResultFormat); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testBasicTableWithOnlyFirstChunk() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testBasicTableWithOnlyFirstChunk(String queryResultFormat) throws Throwable { // Result only includes first data chunk, test maxSize is small. - testBasicTableHarness(1, 1, "", true, false); + testBasicTableHarness(1, 1, "", true, false, queryResultFormat); // Test Async mode - testBasicTableHarness(1, 1, "", true, true); + testBasicTableHarness(1, 1, "", true, true, queryResultFormat); // Result only includes first data chunk, test maxSize is big. - testBasicTableHarness(1, 1024 * 1024, "", false, false); + testBasicTableHarness(1, 1024 * 1024, "", false, false, queryResultFormat); // Test async mode - testBasicTableHarness(1, 1024 * 1024, "", false, true); + testBasicTableHarness(1, 1024 * 1024, "", false, true, queryResultFormat); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testBasicTableWithOneFileChunk() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testBasicTableWithOneFileChunk(String queryResultFormat) throws Throwable { // Result only includes first data chunk, test maxSize is small. - testBasicTableHarness(300, 1, "", true, false); + testBasicTableHarness(300, 1, "", true, false, queryResultFormat); // Test Async mode - testBasicTableHarness(300, 1, "", true, true); + testBasicTableHarness(300, 1, "", true, true, queryResultFormat); // Result only includes first data chunk, test maxSize is big. - testBasicTableHarness(300, 1024 * 1024, "", false, false); + testBasicTableHarness(300, 1024 * 1024, "", false, false, queryResultFormat); // Test Async mode - testBasicTableHarness(300, 1024 * 1024, "", false, true); + testBasicTableHarness(300, 1024 * 1024, "", false, true, queryResultFormat); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testBasicTableWithSomeFileChunks() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testBasicTableWithSomeFileChunks(String queryResultFormat) throws Throwable { // Result only includes first data chunk, test maxSize is small. - testBasicTableHarness(90000, 1, "", true, false); + testBasicTableHarness(90000, 1, "", true, false, queryResultFormat); // Test Async mode - testBasicTableHarness(90000, 1, "", true, true); + testBasicTableHarness(90000, 1, "", true, true, queryResultFormat); // Result only includes first data chunk, test maxSize is median. - testBasicTableHarness(90000, 3 * 1024 * 1024, "", false, false); + testBasicTableHarness(90000, 3 * 1024 * 1024, "", false, false, queryResultFormat); // Test Async mode - testBasicTableHarness(90000, 3 * 1024 * 1024, "", false, true); + testBasicTableHarness(90000, 3 * 1024 * 1024, "", false, true, queryResultFormat); // Result only includes first data chunk, test maxSize is big. - testBasicTableHarness(90000, 100 * 1024 * 1024, "", false, false); + testBasicTableHarness(90000, 100 * 1024 * 1024, "", false, false, queryResultFormat); // Test Async mode - testBasicTableHarness(90000, 100 * 1024 * 1024, "", false, true); + testBasicTableHarness(90000, 100 * 1024 * 1024, "", false, true, queryResultFormat); } /** @@ -365,11 +366,12 @@ private void testTimestampHarness( String format_ntz, String format_ltz, String format_tz, - String timezone) + String timezone, + String queryResultFormat) throws Throwable { List fileNameList = null; String originalResultCSVString = null; - try (Connection connection = init(); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute("alter session set DATE_OUTPUT_FORMAT = '" + format_date + "'"); statement.execute("alter session set TIME_OUTPUT_FORMAT = '" + format_time + "'"); @@ -419,9 +421,10 @@ private void testTimestampHarness( assertEquals(chunkResultString, originalResultCSVString); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testTimestamp() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testTimestamp(String queryResultFormat) throws Throwable { String[] dateFormats = {"YYYY-MM-DD", "DD-MON-YYYY", "MM/DD/YYYY"}; String[] timeFormats = {"HH24:MI:SS.FFTZH:TZM", "HH24:MI:SS.FF", "HH24:MI:SS"}; String[] timestampFormats = { @@ -441,16 +444,19 @@ public void testTimestamp() throws Throwable { timestampFormats[i], timestampFormats[i], timestampFormats[i], - timezones[i]); + timezones[i], + queryResultFormat); } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testBasicTableWithSerializeObjectsAfterReadResultSet() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testBasicTableWithSerializeObjectsAfterReadResultSet(String queryResultFormat) + throws Throwable { List fileNameList = null; String originalResultCSVString = null; - try (Connection connection = init(); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute("create or replace schema testschema"); @@ -528,13 +534,14 @@ private synchronized List splitResultSetSerializables( return resultFileList; } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testSplitResultSetSerializable() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testSplitResultSetSerializable(String queryResultFormat) throws Throwable { List fileNameList = null; String originalResultCSVString = null; int rowCount = 90000; - try (Connection connection = init(); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute( @@ -594,10 +601,11 @@ private void hackToSetupWrongURL(List resultSetS } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testCloseUnconsumedResultSet() throws Throwable { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testCloseUnconsumedResultSet(String queryResultFormat) throws Throwable { + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { try { statement.execute( @@ -624,13 +632,14 @@ public void testCloseUnconsumedResultSet() throws Throwable { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testNegativeWithChunkFileNotExist() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testNegativeWithChunkFileNotExist(String queryResultFormat) throws Throwable { // This test takes about (download worker retry times * networkTimeout) long to finish Properties properties = new Properties(); properties.put("networkTimeout", 10000); // 10000 millisec - try (Connection connection = init(properties)) { + try (Connection connection = init(properties, queryResultFormat)) { try (Statement statement = connection.createStatement()) { statement.execute( "create or replace table table_basic " + " (int_c int, string_c string(128))"); @@ -678,10 +687,11 @@ public void testNegativeWithChunkFileNotExist() throws Throwable { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testNegativeWithClosedResultSet() throws Throwable { - try (Connection connection = init()) { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testNegativeWithClosedResultSet(String queryResultFormat) throws Throwable { + try (Connection connection = init(queryResultFormat)) { Statement statement = connection.createStatement(); statement.execute( @@ -730,15 +740,16 @@ public void testNegativeWithClosedResultSet() throws Throwable { * * @throws Throwable */ - @Test - @Ignore - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testCustomProxyWithFiles() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @Disabled + @DontRunOnGithubActions + public void testCustomProxyWithFiles(String queryResultFormat) throws Throwable { boolean generateFiles = false; boolean correctProxy = false; if (generateFiles) { - generateTestFiles(); + generateTestFiles(queryResultFormat); fail("This is generate test file."); } @@ -775,8 +786,8 @@ public void testCustomProxyWithFiles() throws Throwable { } } - private void generateTestFiles() throws Throwable { - try (Connection connection = init(); + private void generateTestFiles(String queryResultFormat) throws Throwable { + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute( @@ -800,15 +811,16 @@ private void generateTestFiles() throws Throwable { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testRetrieveMetadata() throws Throwable { + @ParameterizedTest + @ArgumentsSource(SimpleResultFormatProvider.class) + @DontRunOnGithubActions + public void testRetrieveMetadata(String queryResultFormat) throws Throwable { List fileNameList; int rowCount = 90000; long expectedTotalRowCount = 0; long expectedTotalCompressedSize = 0; long expectedTotalUncompressedSize = 0; - try (Connection connection = init(); + try (Connection connection = init(queryResultFormat); Statement statement = connection.createStatement()) { statement.execute( diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeS3ClientHandleExceptionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeS3ClientHandleExceptionLatestIT.java index aed4d1f39..e104abc66 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeS3ClientHandleExceptionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeS3ClientHandleExceptionLatestIT.java @@ -3,6 +3,10 @@ */ package net.snowflake.client.jdbc; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.ClientConfiguration; @@ -16,27 +20,24 @@ import java.sql.SQLException; import java.sql.Statement; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.Constants; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.SFStatement; import net.snowflake.client.jdbc.cloud.storage.SnowflakeS3Client; import net.snowflake.client.jdbc.cloud.storage.StageInfo; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.mockito.Mockito; /** Test for SnowflakeS3Client handle exception function */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class SnowflakeS3ClientHandleExceptionLatestIT extends AbstractDriverIT { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; private Connection connection; private SFStatement sfStatement; private SFSession sfSession; @@ -46,7 +47,7 @@ public class SnowflakeS3ClientHandleExceptionLatestIT extends AbstractDriverIT { private int maxRetry; private static final String EXPIRED_AWS_TOKEN_ERROR_CODE = "ExpiredToken"; - @Before + @BeforeEach public void setup() throws SQLException { connection = getConnection("s3testaccount"); sfSession = connection.unwrap(SnowflakeConnectionV1.class).getSfSession(); @@ -75,7 +76,7 @@ public void setup() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void errorRenewExpired() throws SQLException, InterruptedException { AmazonS3Exception ex = new AmazonS3Exception("unauthenticated"); ex.setErrorCode(EXPIRED_AWS_TOKEN_ERROR_CODE); @@ -100,19 +101,27 @@ public void run() { thread.start(); thread.interrupt(); thread.join(); - Assert.assertNull("Exception must not have been thrown in here", exceptionContainer[0]); + assertNull(exceptionContainer[0], "Exception must not have been thrown in here"); Mockito.verify(spyingClient, Mockito.times(2)).renew(Mockito.anyMap()); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorNotFound() throws SQLException { - spyingClient.handleStorageException( - new AmazonS3Exception("Not found"), overMaxRetry, "upload", sfSession, command, null); + @Test + @DontRunOnGithubActions + public void errorNotFound() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new AmazonS3Exception("Not found"), + overMaxRetry, + "upload", + sfSession, + command, + null)); } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void errorBadRequestTokenExpired() throws SQLException { AmazonServiceException ex = new AmazonServiceException("Bad Request"); ex.setServiceName("Amazon S3"); @@ -126,91 +135,113 @@ public void errorBadRequestTokenExpired() throws SQLException { Mockito.verify(spyingClient, Mockito.times(1)).renew(Mockito.anyMap()); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorClientUnknown() throws SQLException { - spyingClient.handleStorageException( - new AmazonClientException("Not found", new IOException()), - overMaxRetry, - "upload", - sfSession, - command, - null); + @Test + @DontRunOnGithubActions + public void errorClientUnknown() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new AmazonClientException("Not found", new IOException()), + overMaxRetry, + "upload", + sfSession, + command, + null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorInvalidKey() throws SQLException { + @Test + @DontRunOnGithubActions + public void errorInvalidKey() { // Unauthenticated, renew is called. - spyingClient.handleStorageException( - new Exception(new InvalidKeyException()), 0, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new Exception(new InvalidKeyException()), 0, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorInterruptedException() throws SQLException { + @Test + @DontRunOnGithubActions + public void errorInterruptedException() throws SnowflakeSQLException { // Can still retry, no error thrown try { spyingClient.handleStorageException( new InterruptedException(), 0, "upload", sfSession, command, null); } catch (Exception e) { - Assert.fail("Should not have exception here"); + fail("Should not have exception here"); } Mockito.verify(spyingClient, Mockito.never()).renew(Mockito.anyMap()); - spyingClient.handleStorageException( - new InterruptedException(), 26, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new InterruptedException(), 26, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorSocketTimeoutException() throws SQLException { + @Test + @DontRunOnGithubActions + public void errorSocketTimeoutException() throws SnowflakeSQLException { // Can still retry, no error thrown try { spyingClient.handleStorageException( new SocketTimeoutException(), 0, "upload", sfSession, command, null); } catch (Exception e) { - Assert.fail("Should not have exception here"); + fail("Should not have exception here"); } Mockito.verify(spyingClient, Mockito.never()).renew(Mockito.anyMap()); - spyingClient.handleStorageException( - new SocketTimeoutException(), 26, "upload", sfSession, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new SocketTimeoutException(), 26, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorUnknownException() throws SQLException { - spyingClient.handleStorageException(new Exception(), 0, "upload", sfSession, command, null); + @Test + @DontRunOnGithubActions + public void errorUnknownException() { + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new Exception(), 0, "upload", sfSession, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorRenewExpiredNullSession() throws SQLException { + @Test + @DontRunOnGithubActions + public void errorRenewExpiredNullSession() { // Unauthenticated, renew is called. AmazonS3Exception ex = new AmazonS3Exception("unauthenticated"); ex.setErrorCode(EXPIRED_AWS_TOKEN_ERROR_CODE); - spyingClient.handleStorageException(ex, 0, "upload", null, command, null); + assertThrows( + SnowflakeSQLException.class, + () -> spyingClient.handleStorageException(ex, 0, "upload", null, command, null)); } - @Test(expected = SnowflakeSQLException.class) - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void errorNoSpaceLeftOnDevice() throws SQLException, IOException { - File destFolder = tmpFolder.newFolder(); + @Test + @DontRunOnGithubActions + public void errorNoSpaceLeftOnDevice() throws IOException { + File destFolder = new File(tmpFolder, "dest"); + destFolder.mkdirs(); String destFolderCanonicalPath = destFolder.getCanonicalPath(); String getCommand = "get @testPutGet_stage/" + TEST_DATA_FILE + " 'file://" + destFolderCanonicalPath + "'"; - spyingClient.handleStorageException( - new StorageException( - maxRetry, - Constants.NO_SPACE_LEFT_ON_DEVICE_ERR, - new IOException(Constants.NO_SPACE_LEFT_ON_DEVICE_ERR)), - 0, - "download", - null, - getCommand, - null); + assertThrows( + SnowflakeSQLException.class, + () -> + spyingClient.handleStorageException( + new StorageException( + maxRetry, + Constants.NO_SPACE_LEFT_ON_DEVICE_ERR, + new IOException(Constants.NO_SPACE_LEFT_ON_DEVICE_ERR)), + 0, + "download", + null, + getCommand, + null)); } - @After + @AfterEach public void cleanUp() throws SQLException { sfStatement.close(); connection.close(); diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeSerializableTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeSerializableTest.java index 4cd2fa7e8..92d00affc 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeSerializableTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeSerializableTest.java @@ -2,10 +2,10 @@ import static net.snowflake.client.jdbc.SnowflakeChunkDownloader.NoOpChunkDownloader; import static net.snowflake.client.jdbc.SnowflakeResultSetSerializableV1.ChunkFileMetadata; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -16,7 +16,7 @@ import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SFBaseStatement; import net.snowflake.client.core.SFStatementType; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SnowflakeSerializableTest { diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeTimestampWithTimezoneTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeTimestampWithTimezoneTest.java index ebf32dcef..41bee6e85 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeTimestampWithTimezoneTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeTimestampWithTimezoneTest.java @@ -3,88 +3,81 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.Timestamp; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.TimeZone; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import java.util.stream.Stream; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; /** * Tests SnowflakeTimestampWithTimezone to ensure the output is not impacted by Day Light Saving * Time. Not this test case is not thread safe, because TimeZone.setDefault is called. */ -@RunWith(Parameterized.class) public class SnowflakeTimestampWithTimezoneTest extends BaseJDBCTest { private static TimeZone orgTimeZone; - private final String timeZone; - private final String inputTimestamp; - private final String outputTimestamp; + static class Params implements ArgumentsProvider { + public Stream provideArguments(ExtensionContext context) { + String[] timeZoneList = {"PST", "America/New_York", "UTC", "Asia/Singapore"}; - public SnowflakeTimestampWithTimezoneTest( - String timeZone, String inputTimestamp, String outputTimestamp) { - this.timeZone = timeZone; - this.inputTimestamp = inputTimestamp; - this.outputTimestamp = outputTimestamp; - } - - @Parameterized.Parameters(name = "tz={0}, input={1}, output={2}") - public static Collection convert() { - String[] timeZoneList = {"PST", "America/New_York", "UTC", "Asia/Singapore"}; + String[] dateTimeList = { + "2018-03-11 01:10:34.0123456", + "2018-03-11 02:10:34.0123456", + "2018-03-11 03:10:34.0123456", + "2018-11-04 01:10:34.123", + "2018-11-04 02:10:34.123", + "2018-11-04 03:10:34.123", + "2020-03-11 01:10:34.456", + "2020-03-11 02:10:34.456", + "2020-03-11 03:10:34.456", + "2020-11-01 01:10:34.123", + "2020-11-01 02:10:34.123", + "2020-11-01 03:10:34.123" + }; - String[] dateTimeList = { - "2018-03-11 01:10:34.0123456", - "2018-03-11 02:10:34.0123456", - "2018-03-11 03:10:34.0123456", - "2018-11-04 01:10:34.123", - "2018-11-04 02:10:34.123", - "2018-11-04 03:10:34.123", - "2020-03-11 01:10:34.456", - "2020-03-11 02:10:34.456", - "2020-03-11 03:10:34.456", - "2020-11-01 01:10:34.123", - "2020-11-01 02:10:34.123", - "2020-11-01 03:10:34.123" - }; - - List testCases = new ArrayList<>(); - for (String timeZone : timeZoneList) { - for (String dateTime : dateTimeList) { - testCases.add(new Object[] {timeZone, dateTime, dateTime}); + List testCases = new ArrayList<>(); + for (String timeZone : timeZoneList) { + for (String dateTime : dateTimeList) { + testCases.add(Arguments.of(timeZone, dateTime, dateTime)); + } } + return testCases.stream(); } - return testCases; } /** Records the original TimeZone */ - @BeforeClass + @BeforeAll public static void keepOriginalTimeZone() { orgTimeZone = TimeZone.getDefault(); } - @AfterClass + @AfterAll public static void restoreTimeZone() { TimeZone.setDefault(orgTimeZone); } - @Test - public void testTimestampNTZ() throws Throwable { + @ParameterizedTest(name = "{index}: {1} {0}") + @ArgumentsSource(Params.class) + public void testTimestampNTZ(String timeZone, String inputTimestamp, String outputTimestamp) { TimeZone.setDefault(TimeZone.getTimeZone(timeZone)); - LocalDateTime dt = parseTimestampNTZ(this.inputTimestamp); + LocalDateTime dt = parseTimestampNTZ(inputTimestamp); SnowflakeTimestampWithTimezone stn = new SnowflakeTimestampWithTimezone( dt.toEpochSecond(ZoneOffset.UTC) * 1000, dt.getNano(), TimeZone.getTimeZone("UTC")); - assertEquals(this.outputTimestamp, stn.toString()); + assertEquals(outputTimestamp, stn.toString()); } @Test diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeTypeTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeTypeTest.java index 29c58b787..b24825c96 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeTypeTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeTypeTest.java @@ -2,15 +2,16 @@ import static net.snowflake.client.jdbc.SnowflakeType.convertStringToType; import static net.snowflake.client.jdbc.SnowflakeType.getJavaType; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.math.BigDecimal; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.sql.Time; import java.sql.Types; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SnowflakeTypeTest { @@ -97,7 +98,7 @@ public void testJavaTypeToSFType() throws SnowflakeSQLException { @Test public void testJavaTypeToClassName() throws SQLException { assertEquals(SnowflakeType.javaTypeToClassName(Types.DECIMAL), BigDecimal.class.getName()); - assertEquals(SnowflakeType.javaTypeToClassName(Types.TIME), java.sql.Time.class.getName()); + assertEquals(SnowflakeType.javaTypeToClassName(Types.TIME), Time.class.getName()); assertEquals(SnowflakeType.javaTypeToClassName(Types.BOOLEAN), Boolean.class.getName()); assertThrows( SQLFeatureNotSupportedException.class, diff --git a/src/test/java/net/snowflake/client/jdbc/SnowflakeUtilTest.java b/src/test/java/net/snowflake/client/jdbc/SnowflakeUtilTest.java index 703e55b7c..054aef9fe 100644 --- a/src/test/java/net/snowflake/client/jdbc/SnowflakeUtilTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SnowflakeUtilTest.java @@ -6,10 +6,10 @@ import static net.snowflake.client.jdbc.SnowflakeUtil.createCaseInsensitiveMap; import static net.snowflake.client.jdbc.SnowflakeUtil.extractColumnMetadata; import static net.snowflake.client.jdbc.SnowflakeUtil.getSnowflakeType; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -21,14 +21,14 @@ import java.util.HashMap; import java.util.Map; import java.util.TreeMap; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ObjectMapperFactory; import org.apache.http.Header; import org.apache.http.message.BasicHeader; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class SnowflakeUtilTest extends BaseJDBCTest { private static final ObjectMapper OBJECT_MAPPER = ObjectMapperFactory.getObjectMapper(); diff --git a/src/test/java/net/snowflake/client/jdbc/SqlFeatureNotSupportedTelemetryTest.java b/src/test/java/net/snowflake/client/jdbc/SqlFeatureNotSupportedTelemetryTest.java index 37819457c..03fa47418 100644 --- a/src/test/java/net/snowflake/client/jdbc/SqlFeatureNotSupportedTelemetryTest.java +++ b/src/test/java/net/snowflake/client/jdbc/SqlFeatureNotSupportedTelemetryTest.java @@ -1,10 +1,10 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.fasterxml.jackson.databind.node.ObjectNode; import net.minidev.json.JSONObject; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SqlFeatureNotSupportedTelemetryTest { diff --git a/src/test/java/net/snowflake/client/jdbc/StatementAlreadyClosedIT.java b/src/test/java/net/snowflake/client/jdbc/StatementAlreadyClosedIT.java index 08bf2ed72..268173e92 100644 --- a/src/test/java/net/snowflake/client/jdbc/StatementAlreadyClosedIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StatementAlreadyClosedIT.java @@ -3,17 +3,17 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class StatementAlreadyClosedIT extends BaseJDBCTest { @Test public void testStatementAlreadyClosed() throws Throwable { diff --git a/src/test/java/net/snowflake/client/jdbc/StatementArrowIT.java b/src/test/java/net/snowflake/client/jdbc/StatementArrowIT.java index 061edb528..f66bbb7c2 100644 --- a/src/test/java/net/snowflake/client/jdbc/StatementArrowIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StatementArrowIT.java @@ -1,9 +1,9 @@ package net.snowflake.client.jdbc; -import net.snowflake.client.category.TestCategoryArrow; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; -@Category(TestCategoryArrow.class) +@Tag(TestTags.ARROW) public class StatementArrowIT extends StatementIT { public StatementArrowIT() { super(); diff --git a/src/test/java/net/snowflake/client/jdbc/StatementFeatureNotSupportedIT.java b/src/test/java/net/snowflake/client/jdbc/StatementFeatureNotSupportedIT.java index 01be27150..b6c62ddc1 100644 --- a/src/test/java/net/snowflake/client/jdbc/StatementFeatureNotSupportedIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StatementFeatureNotSupportedIT.java @@ -6,11 +6,11 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class StatementFeatureNotSupportedIT extends BaseJDBCTest { @Test public void testFeatureNotSupportedException() throws Throwable { diff --git a/src/test/java/net/snowflake/client/jdbc/StatementIT.java b/src/test/java/net/snowflake/client/jdbc/StatementIT.java index 2fa713308..075889834 100644 --- a/src/test/java/net/snowflake/client/jdbc/StatementIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StatementIT.java @@ -6,13 +6,13 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.sql.BatchUpdateException; @@ -24,21 +24,19 @@ import java.time.Duration; import java.util.List; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryStatement; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.telemetry.Telemetry; import net.snowflake.client.jdbc.telemetry.TelemetryClient; import net.snowflake.common.core.SqlState; import org.awaitility.Awaitility; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** Statement tests */ -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class StatementIT extends BaseJDBCWithSharedConnectionIT { protected static String queryResultFormat = "json"; @@ -50,7 +48,7 @@ public static Connection getConnection() throws SQLException { return conn; } - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; @Test public void testFetchDirection() throws SQLException { @@ -64,7 +62,7 @@ public void testFetchDirection() throws SQLException { } } - @Ignore("Not working for setFetchSize") + @Disabled("Not working for setFetchSize") @Test public void testFetchSize() throws SQLException { try (Statement statement = connection.createStatement()) { @@ -362,7 +360,8 @@ public void testExecuteBatch() throws Exception { "put file://" + getFullPathFileInResource(TEST_DATA_FILE) + " @%test_batch auto_compress=false"); - File tempFolder = tmpFolder.newFolder("test_downloads_folder"); + File tempFolder = new File(tmpFolder, "test_downloads_folder"); + tempFolder.mkdirs(); statement.addBatch("get @%test_batch file://" + tempFolder.getCanonicalPath()); rowCounts = statement.executeBatch(); @@ -423,7 +422,7 @@ public void testExecuteLargeBatch() throws SQLException { * @throws SQLException if any error occurs */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testExecuteUpdateZeroCount() throws SQLException { try (Connection connection = getConnection()) { String[] testCommands = { diff --git a/src/test/java/net/snowflake/client/jdbc/StatementLargeUpdateIT.java b/src/test/java/net/snowflake/client/jdbc/StatementLargeUpdateIT.java index d041b1694..b0eefd096 100644 --- a/src/test/java/net/snowflake/client/jdbc/StatementLargeUpdateIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StatementLargeUpdateIT.java @@ -1,15 +1,15 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.Connection; import java.sql.Statement; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Large update test. No JSON/ARROW specific test case is required. */ -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class StatementLargeUpdateIT extends BaseJDBCTest { @Test public void testLargeUpdate() throws Throwable { diff --git a/src/test/java/net/snowflake/client/jdbc/StatementLatestIT.java b/src/test/java/net/snowflake/client/jdbc/StatementLatestIT.java index 9d96f44ea..e2f030464 100644 --- a/src/test/java/net/snowflake/client/jdbc/StatementLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StatementLatestIT.java @@ -6,11 +6,11 @@ import static net.snowflake.client.jdbc.ErrorCode.ROW_DOES_NOT_EXIST; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.net.URL; @@ -22,17 +22,15 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; import net.snowflake.client.TestUtil; -import net.snowflake.client.category.TestCategoryStatement; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.ParameterBindingDTO; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.bind.BindUploader; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** * Statement integration tests for the latest JDBC driver. This doesn't work for the oldest @@ -40,7 +38,7 @@ * if the tests still is not applicable. If it is applicable, move tests to StatementIT so that both * the latest and oldest supported driver run the tests. */ -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class StatementLatestIT extends BaseJDBCWithSharedConnectionIT { protected static String queryResultFormat = "json"; @@ -52,7 +50,7 @@ public static Connection getConnection() throws SQLException { return conn; } - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; @Test public void testExecuteCreateAndDrop() throws SQLException { @@ -83,9 +81,10 @@ public void testExecuteCreateAndDrop() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testCopyAndUpload() throws Exception { - File tempFolder = tmpFolder.newFolder("test_downloads_folder"); + File tempFolder = new File(tmpFolder, "test_downloads_folder"); + tempFolder.mkdirs(); List accounts = Arrays.asList(null, "s3testaccount", "azureaccount", "gcpaccount"); for (int i = 0; i < accounts.size(); i++) { String fileName = "test_copy.csv"; @@ -198,7 +197,7 @@ public void testExecuteOpenResultSets() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPreparedStatementLogging() throws SQLException { try (Connection con = getConnection(); Statement stmt = con.createStatement()) { diff --git a/src/test/java/net/snowflake/client/jdbc/StatementNoOpLatestIT.java b/src/test/java/net/snowflake/client/jdbc/StatementNoOpLatestIT.java index 12aa69882..22b58584d 100644 --- a/src/test/java/net/snowflake/client/jdbc/StatementNoOpLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StatementNoOpLatestIT.java @@ -4,17 +4,17 @@ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; -import net.snowflake.client.category.TestCategoryStatement; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryStatement.class) +@Tag(TestTags.STATEMENT) public class StatementNoOpLatestIT { @Test public void testSnowflakeNoOpStatement() throws SQLException { diff --git a/src/test/java/net/snowflake/client/jdbc/StreamIT.java b/src/test/java/net/snowflake/client/jdbc/StreamIT.java index d1762904d..e6407c16c 100644 --- a/src/test/java/net/snowflake/client/jdbc/StreamIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StreamIT.java @@ -3,8 +3,8 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.InputStream; import java.io.StringWriter; @@ -14,15 +14,14 @@ import java.sql.Statement; import java.util.Arrays; import java.util.List; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import org.apache.commons.io.IOUtils; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Stream interface tests. Snowflake JDBC specific API */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class StreamIT extends BaseJDBCTest { /** * Test Upload Stream @@ -53,7 +52,7 @@ public void testUploadStream() throws Throwable { while (rset.next()) { ret = rset.getString(1); } - assertEquals("Unexpected string value: " + ret + " expect: hello", "hello", ret); + assertEquals("hello", ret, "Unexpected string value: " + ret + " expect: hello"); } } finally { statement.execute("rm @~/" + DEST_PREFIX); @@ -69,7 +68,7 @@ public void testUploadStream() throws Throwable { * @throws Throwable if any error occurs. */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDownloadStream() throws Throwable { final String DEST_PREFIX = TEST_UUID + "/testUploadStream"; List supportedAccounts = Arrays.asList("s3testaccount", "azureaccount"); @@ -132,7 +131,7 @@ public void testCompressAndUploadStream() throws Throwable { while (rset.next()) { ret = rset.getString(1); } - assertEquals("Unexpected string value: " + ret + " expect: hello", "hello", ret); + assertEquals("hello", ret, "Unexpected string value: " + ret + " expect: hello"); } } finally { diff --git a/src/test/java/net/snowflake/client/jdbc/StreamLatestIT.java b/src/test/java/net/snowflake/client/jdbc/StreamLatestIT.java index 093c2de27..af7c8eea3 100644 --- a/src/test/java/net/snowflake/client/jdbc/StreamLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/StreamLatestIT.java @@ -3,9 +3,9 @@ */ package net.snowflake.client.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.BufferedWriter; import java.io.File; @@ -19,15 +19,13 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import org.apache.commons.io.IOUtils; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** * Stream API tests for the latest JDBC driver. This doesn't work for the oldest supported driver. @@ -35,10 +33,10 @@ * is not applicable. If it is applicable, move tests to StreamIT so that both the latest and oldest * supported driver run the tests. */ -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class StreamLatestIT extends BaseJDBCTest { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); + @TempDir private File tmpFolder; /** * Test Upload Stream with atypical stage names @@ -72,7 +70,7 @@ public void testUnusualStageName() throws Throwable { while (rset.next()) { ret = rset.getString(1); } - assertEquals("Unexpected string value: " + ret + " expect: hello", "hello", ret); + assertEquals("hello", ret, "Unexpected string value: " + ret + " expect: hello"); } statement.execute("CREATE or replace TABLE \"ice cream (nice)\" (types STRING)"); @@ -92,7 +90,7 @@ public void testUnusualStageName() throws Throwable { while (rset.next()) { ret = rset.getString(1); } - assertEquals("Unexpected string value: " + ret + " expect: hello", "hello", ret); + assertEquals("hello", ret, "Unexpected string value: " + ret + " expect: hello"); } } finally { statement.execute("DROP TABLE IF EXISTS \"ice cream (nice)\""); @@ -101,7 +99,7 @@ public void testUnusualStageName() throws Throwable { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDownloadToStreamBlobNotFoundGCS() throws SQLException { final String DEST_PREFIX = TEST_UUID + "/testUploadStream"; Properties paramProperties = new Properties(); @@ -118,8 +116,8 @@ public void testDownloadToStreamBlobNotFoundGCS() throws SQLException { } catch (Exception ex) { assertTrue(ex instanceof SQLException); assertTrue( - "Wrong exception message: " + ex.getMessage(), - ex.getMessage().contains("File not found")); + ex.getMessage().contains("File not found"), + "Wrong exception message: " + ex.getMessage()); } finally { statement.execute("rm @~/" + DEST_PREFIX); } @@ -127,7 +125,7 @@ public void testDownloadToStreamBlobNotFoundGCS() throws SQLException { } @Test - @Ignore + @Disabled public void testDownloadToStreamGCSPresignedUrl() throws SQLException, IOException { final String DEST_PREFIX = "testUploadStream"; @@ -141,7 +139,7 @@ public void testDownloadToStreamGCSPresignedUrl() throws SQLException, IOExcepti + " @testgcpstage/" + DEST_PREFIX)) { assertTrue(rset.next()); - assertEquals("Error message:" + rset.getString(8), "UPLOADED", rset.getString(7)); + assertEquals("UPLOADED", rset.getString(7), "Error message:" + rset.getString(8)); InputStream out = connection @@ -162,7 +160,7 @@ public void testDownloadToStreamGCSPresignedUrl() throws SQLException, IOExcepti } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDownloadToStreamGCS() throws SQLException, IOException { final String DEST_PREFIX = TEST_UUID + "/testUploadStream"; Properties paramProperties = new Properties(); @@ -202,7 +200,8 @@ public void testSpecialCharactersInFileName() throws SQLException, IOException { Statement statement = connection.createStatement()) { try { // Create a temporary file with special characters in the name and write to it - File specialCharFile = tmpFolder.newFile("(special char@).txt"); + File specialCharFile = new File(tmpFolder, "(special char@).txt"); + specialCharFile.createNewFile(); try (BufferedWriter bw = new BufferedWriter(new FileWriter(specialCharFile))) { bw.write("Creating test file for downloadStream test"); } diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/CloudStorageClientLatestIT.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/CloudStorageClientLatestIT.java index 20a070a02..d09182860 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/CloudStorageClientLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/CloudStorageClientLatestIT.java @@ -2,26 +2,28 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.UUID; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.BaseJDBCTest; import net.snowflake.client.jdbc.SnowflakeConnection; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class CloudStorageClientLatestIT extends BaseJDBCTest { /** * Test for SNOW-565154 - it was waiting for ~5 minutes so the test is waiting much shorter time */ - @Test(timeout = 30000L) + @Test + @Timeout(30) public void testDownloadStreamShouldFailFastOnNotExistingFile() throws Throwable { String stageName = "testDownloadStream_stage_" + UUID.randomUUID().toString().replaceAll("-", "_"); diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/EncryptionProviderTest.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/EncryptionProviderTest.java index a560d1f81..61cd07769 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/EncryptionProviderTest.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/EncryptionProviderTest.java @@ -1,6 +1,6 @@ package net.snowflake.client.jdbc.cloud.storage; -import static org.junit.Assert.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -16,8 +16,8 @@ import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; public class EncryptionProviderTest { @@ -41,7 +41,7 @@ public class EncryptionProviderTest { byte[] plainText = "the quick brown fox jumps over the lazy dog".getBytes(StandardCharsets.UTF_8); - @Before + @BeforeEach public void setUp() { encMat.setQueryStageMasterKey(queryStageMasterKey); encMat.setSmkId(123); diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/GcmEncryptionProviderTest.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/GcmEncryptionProviderTest.java index b853ef639..f883324d3 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/GcmEncryptionProviderTest.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/GcmEncryptionProviderTest.java @@ -1,8 +1,8 @@ package net.snowflake.client.jdbc.cloud.storage; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -25,8 +25,8 @@ import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; public class GcmEncryptionProviderTest { @@ -58,7 +58,7 @@ public class GcmEncryptionProviderTest { byte[] dataAad = "data aad".getBytes(StandardCharsets.UTF_8); byte[] keyAad = "key aad".getBytes(StandardCharsets.UTF_8); - @Before + @BeforeEach public void setUp() { encMat.setQueryStageMasterKey(queryStageMasterKey); encMat.setSmkId(123); @@ -138,21 +138,21 @@ public void testDecryptStreamWithInvalidKey() throws Exception { InputStream plainTextStream = new ByteArrayInputStream(plainText); byte[] cipherText = encryptStream(plainTextStream, dataAad, keyAad); + + byte[] encryptedKey = encKeyArgumentCaptor.getValue(); + encryptedKey[0] = (byte) ((encryptedKey[0] + 1) % 255); assertThrows( AEADBadTagException.class, - () -> { - byte[] encryptedKey = encKeyArgumentCaptor.getValue(); - encryptedKey[0] = (byte) ((encryptedKey[0] + 1) % 255); - IOUtils.toByteArray( - GcmEncryptionProvider.decryptStream( - new ByteArrayInputStream(cipherText), - Base64.getEncoder().encodeToString(encryptedKey), - Base64.getEncoder().encodeToString(dataIvDataArgumentCaptor.getValue()), - Base64.getEncoder().encodeToString(keyIvDataArgumentCaptor.getValue()), - encMat, - dataAad == null ? "" : Base64.getEncoder().encodeToString(dataAad), - keyAad == null ? "" : Base64.getEncoder().encodeToString(keyAad))); - }); + () -> + IOUtils.toByteArray( + GcmEncryptionProvider.decryptStream( + new ByteArrayInputStream(cipherText), + Base64.getEncoder().encodeToString(encryptedKey), + Base64.getEncoder().encodeToString(dataIvDataArgumentCaptor.getValue()), + Base64.getEncoder().encodeToString(keyIvDataArgumentCaptor.getValue()), + encMat, + dataAad == null ? "" : Base64.getEncoder().encodeToString(dataAad), + keyAad == null ? "" : Base64.getEncoder().encodeToString(keyAad)))); } @Test @@ -160,12 +160,12 @@ public void testDecryptStreamWithInvalidDataIV() throws Exception { InputStream plainTextStream = new ByteArrayInputStream(plainText); byte[] cipherText = encryptStream(plainTextStream, dataAad, keyAad); + byte[] dataIvBase64 = dataIvDataArgumentCaptor.getValue(); + dataIvBase64[0] = (byte) ((dataIvBase64[0] + 1) % 255); IOException ioException = assertThrows( IOException.class, () -> { - byte[] dataIvBase64 = dataIvDataArgumentCaptor.getValue(); - dataIvBase64[0] = (byte) ((dataIvBase64[0] + 1) % 255); IOUtils.toByteArray( GcmEncryptionProvider.decryptStream( new ByteArrayInputStream(cipherText), @@ -184,11 +184,11 @@ public void testDecryptStreamWithInvalidKeyIV() throws Exception { InputStream plainTextStream = new ByteArrayInputStream(plainText); byte[] cipherText = encryptStream(plainTextStream, dataAad, keyAad); + byte[] keyIvBase64 = keyIvDataArgumentCaptor.getValue(); + keyIvBase64[0] = (byte) ((keyIvBase64[0] + 1) % 255); assertThrows( AEADBadTagException.class, () -> { - byte[] keyIvBase64 = keyIvDataArgumentCaptor.getValue(); - keyIvBase64[0] = (byte) ((keyIvBase64[0] + 1) % 255); IOUtils.toByteArray( GcmEncryptionProvider.decryptStream( new ByteArrayInputStream(cipherText), diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientLatestIT.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientLatestIT.java index 05050b669..4bca15b3a 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientLatestIT.java @@ -1,9 +1,9 @@ package net.snowflake.client.jdbc.cloud.storage; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.spy; import com.amazonaws.services.kms.model.UnsupportedOperationException; @@ -11,9 +11,8 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.SFStatement; import net.snowflake.client.jdbc.BaseJDBCTest; @@ -21,13 +20,13 @@ import net.snowflake.client.jdbc.SnowflakeFileTransferAgent; import net.snowflake.client.jdbc.SnowflakeSQLLoggedException; import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class SnowflakeAzureClientLatestIT extends BaseJDBCTest { @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testAzureClientSetupInvalidEncryptionKeySize() throws SQLException { try (Connection connection = getConnection("azureaccount")) { SFSession sfSession = connection.unwrap(SnowflakeConnectionV1.class).getSfSession(); diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientTest.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientTest.java index f0ba5b3d4..efc49f41f 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientTest.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeAzureClientTest.java @@ -4,11 +4,11 @@ package net.snowflake.client.jdbc.cloud.storage; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.microsoft.azure.storage.StorageExtendedErrorInformation; import java.util.LinkedHashMap; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SnowflakeAzureClientTest { @Test diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientLatestIT.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientLatestIT.java index d9e1821c2..b46064778 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientLatestIT.java @@ -3,8 +3,9 @@ */ package net.snowflake.client.jdbc.cloud.storage; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import com.amazonaws.AmazonServiceException; import com.amazonaws.ClientConfiguration; @@ -13,26 +14,24 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.SFSession; import net.snowflake.client.core.SFStatement; import net.snowflake.client.jdbc.BaseJDBCTest; import net.snowflake.client.jdbc.SnowflakeConnectionV1; import net.snowflake.client.jdbc.SnowflakeFileTransferAgent; import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; -@Category(TestCategoryOthers.class) +@Tag(TestTags.OTHERS) public class SnowflakeS3ClientLatestIT extends BaseJDBCTest { @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testS3Client256Encryption() throws SQLException { try (Connection connection = getConnection("s3testaccount")) { SFSession sfSession = connection.unwrap(SnowflakeConnectionV1.class).getSfSession(); @@ -73,7 +72,7 @@ public void testS3Client256Encryption() throws SQLException { * @throws SQLException */ @Test - @Ignore + @Disabled public void testS3ConnectionWithProxyEnvVariablesSet() throws SQLException { String testStageName = "s3TestStage"; @@ -103,7 +102,7 @@ public void testS3ConnectionWithProxyEnvVariablesSet() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testIsClientException400Or404() throws SQLException { AmazonServiceException servEx = new AmazonServiceException("S3 operation failed"); servEx.setServiceName("Amazon S3"); @@ -138,7 +137,7 @@ public void testIsClientException400Or404() throws SQLException { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testPutGetMaxRetries() throws SQLException { Properties props = new Properties(); props.put("putGetMaxRetries", 1); @@ -170,7 +169,7 @@ public void testPutGetMaxRetries() throws SQLException { spy.handleStorageException( new InterruptedException(), 0, "download", sfSession, command, null); } catch (Exception e) { - Assert.fail("Should not have exception here"); + fail("Should not have exception here"); } Mockito.verify(spy, Mockito.never()).renew(Mockito.anyMap()); spy.handleStorageException( diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientTest.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientTest.java index 3daddf3df..91366cbaf 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientTest.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/SnowflakeS3ClientTest.java @@ -3,9 +3,9 @@ */ package net.snowflake.client.jdbc.cloud.storage; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SnowflakeS3ClientTest { diff --git a/src/test/java/net/snowflake/client/jdbc/cloud/storage/StageInfoGcsCustomEndpointTest.java b/src/test/java/net/snowflake/client/jdbc/cloud/storage/StageInfoGcsCustomEndpointTest.java index f8e00d7eb..6b27066c6 100644 --- a/src/test/java/net/snowflake/client/jdbc/cloud/storage/StageInfoGcsCustomEndpointTest.java +++ b/src/test/java/net/snowflake/client/jdbc/cloud/storage/StageInfoGcsCustomEndpointTest.java @@ -3,55 +3,57 @@ */ package net.snowflake.client.jdbc.cloud.storage; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Optional; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import net.snowflake.client.providers.SnowflakeArgumentsProvider; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) public class StageInfoGcsCustomEndpointTest { - private final String region; - private final boolean useRegionalUrl; - private final String endPoint; - private final Optional expectedHost; - public StageInfoGcsCustomEndpointTest( - String region, boolean useRegionalUrl, String endPoint, Optional expectedHost) { - this.region = region; - this.useRegionalUrl = useRegionalUrl; - this.endPoint = endPoint; - this.expectedHost = expectedHost; + private static class DataProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return Arrays.asList( + Arguments.of("US-CENTRAL1", false, null, Optional.empty()), + Arguments.of("US-CENTRAL1", false, "", Optional.empty()), + Arguments.of("US-CENTRAL1", false, "null", Optional.empty()), + Arguments.of("US-CENTRAL1", false, " ", Optional.empty()), + Arguments.of("US-CENTRAL1", false, "example.com", Optional.of("example.com")), + Arguments.of( + "ME-CENTRAL2", false, null, Optional.of("storage.me-central2.rep.googleapis.com")), + Arguments.of( + "ME-CENTRAL2", true, null, Optional.of("storage.me-central2.rep.googleapis.com")), + Arguments.of( + "ME-CENTRAL2", true, "", Optional.of("storage.me-central2.rep.googleapis.com")), + Arguments.of( + "ME-CENTRAL2", true, " ", Optional.of("storage.me-central2.rep.googleapis.com")), + Arguments.of("ME-CENTRAL2", true, "example.com", Optional.of("example.com")), + Arguments.of( + "US-CENTRAL1", true, null, Optional.of("storage.us-central1.rep.googleapis.com")), + Arguments.of( + "US-CENTRAL1", true, "", Optional.of("storage.us-central1.rep.googleapis.com")), + Arguments.of( + "US-CENTRAL1", true, " ", Optional.of("storage.us-central1.rep.googleapis.com")), + Arguments.of( + "US-CENTRAL1", true, "null", Optional.of("storage.us-central1.rep.googleapis.com")), + Arguments.of("US-CENTRAL1", true, "example.com", Optional.of("example.com"))); + } } - @Test - public void shouldReturnEmptyGCSRegionalUrlWhenNotMeCentral1AndNotUseRegionalUrl() { + @ParameterizedTest + @ArgumentsSource(DataProvider.class) + public void shouldReturnEmptyGCSRegionalUrlWhenNotMeCentral1AndNotUseRegionalUrl( + String region, boolean useRegionalUrl, String endPoint, Optional expectedHost) { StageInfo stageInfo = StageInfo.createStageInfo("GCS", "bla", new HashMap<>(), region, endPoint, "account", true); stageInfo.setUseRegionalUrl(useRegionalUrl); assertEquals(expectedHost, stageInfo.gcsCustomEndpoint()); } - - @Parameterized.Parameters() - public static Object[][] data() { - return new Object[][] { - {"US-CENTRAL1", false, null, Optional.empty()}, - {"US-CENTRAL1", false, "", Optional.empty()}, - {"US-CENTRAL1", false, "null", Optional.empty()}, - {"US-CENTRAL1", false, " ", Optional.empty()}, - {"US-CENTRAL1", false, "example.com", Optional.of("example.com")}, - {"ME-CENTRAL2", false, null, Optional.of("storage.me-central2.rep.googleapis.com")}, - {"ME-CENTRAL2", true, null, Optional.of("storage.me-central2.rep.googleapis.com")}, - {"ME-CENTRAL2", true, "", Optional.of("storage.me-central2.rep.googleapis.com")}, - {"ME-CENTRAL2", true, " ", Optional.of("storage.me-central2.rep.googleapis.com")}, - {"ME-CENTRAL2", true, "example.com", Optional.of("example.com")}, - {"US-CENTRAL1", true, null, Optional.of("storage.us-central1.rep.googleapis.com")}, - {"US-CENTRAL1", true, "", Optional.of("storage.us-central1.rep.googleapis.com")}, - {"US-CENTRAL1", true, " ", Optional.of("storage.us-central1.rep.googleapis.com")}, - {"US-CENTRAL1", true, "null", Optional.of("storage.us-central1.rep.googleapis.com")}, - {"US-CENTRAL1", true, "example.com", Optional.of("example.com")}, - }; - } } diff --git a/src/test/java/net/snowflake/client/jdbc/diagnostic/DiagnosticContextLatestIT.java b/src/test/java/net/snowflake/client/jdbc/diagnostic/DiagnosticContextLatestIT.java index 042c6b0f4..8df4f988e 100644 --- a/src/test/java/net/snowflake/client/jdbc/diagnostic/DiagnosticContextLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/diagnostic/DiagnosticContextLatestIT.java @@ -1,8 +1,8 @@ package net.snowflake.client.jdbc.diagnostic; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.net.InetSocketAddress; @@ -11,15 +11,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import net.snowflake.client.category.TestCategoryDiagnostic; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.SFSessionProperty; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryDiagnostic.class) +@Tag(TestTags.DIAGNOSTIC) public class DiagnosticContextLatestIT { private static final String HTTP_NON_PROXY_HOSTS = "http.nonProxyHosts"; @@ -34,7 +34,7 @@ public class DiagnosticContextLatestIT { private static String oldJvmHttpsProxyHost; private static String oldJvmHttpsProxyPort; - @BeforeClass + @BeforeAll public static void init() { oldJvmNonProxyHosts = System.getProperty(HTTP_NON_PROXY_HOSTS); oldJvmHttpProxyHost = System.getProperty(HTTP_PROXY_HOST); @@ -43,7 +43,7 @@ public static void init() { oldJvmHttpsProxyPort = System.getProperty(HTTPS_PROXY_PORT); } - @Before + @BeforeEach public void clearJvmProperties() { System.clearProperty(HTTP_NON_PROXY_HOSTS); System.clearProperty(HTTP_PROXY_HOST); @@ -97,7 +97,7 @@ public void parseAllowListFileTest() { String testFailedMessage = "The lists of SnowflakeEndpoints in mockEndpoints and endpointsFromTestFile should be identical"; - assertTrue(testFailedMessage, endpointsFromTestFile.containsAll(mockEndpoints)); + assertTrue(endpointsFromTestFile.containsAll(mockEndpoints), testFailedMessage); } /** @@ -112,24 +112,24 @@ public void testEmptyProxyConfig() { DiagnosticContext diagnosticContext = new DiagnosticContext(connectionPropertiesMap); - assertFalse("Proxy configurations should be empty", diagnosticContext.isProxyEnabled()); + assertFalse(diagnosticContext.isProxyEnabled(), "Proxy configurations should be empty"); assertTrue( - "getHttpProxyHost() must return an empty string in the absence of proxy configuration", - diagnosticContext.getHttpProxyHost().isEmpty()); + diagnosticContext.getHttpProxyHost().isEmpty(), + "getHttpProxyHost() must return an empty string in the absence of proxy configuration"); assertEquals( - "getHttpProxyPort() must return -1 in the absence of proxy configuration", -1, - diagnosticContext.getHttpProxyPort()); + diagnosticContext.getHttpProxyPort(), + "getHttpProxyPort() must return -1 in the absence of proxy configuration"); assertTrue( - "getHttpsProxyHost() must return an empty string in the absence of proxy configuration", - diagnosticContext.getHttpsProxyHost().isEmpty()); + diagnosticContext.getHttpsProxyHost().isEmpty(), + "getHttpsProxyHost() must return an empty string in the absence of proxy configuration"); assertEquals( - "getHttpsProxyPort() must return -1 in the absence of proxy configuration", -1, - diagnosticContext.getHttpsProxyPort()); + diagnosticContext.getHttpsProxyPort(), + "getHttpsProxyPort() must return -1 in the absence of proxy configuration"); assertTrue( - "getHttpNonProxyHosts() must return an empty string in the absence of proxy configuration", - diagnosticContext.getHttpNonProxyHosts().isEmpty()); + diagnosticContext.getHttpNonProxyHosts().isEmpty(), + "getHttpNonProxyHosts() must return an empty string in the absence of proxy configuration"); } /** Test added in version > 3.16.1 */ @@ -329,7 +329,7 @@ public void testgetNoProxyAfterOverridingJvm() { assertEquals(noProxy, diagnosticContext.getProxy(host4)); } - @After + @AfterEach public void restoreJvmArguments() { System.clearProperty(HTTP_NON_PROXY_HOSTS); System.clearProperty(HTTP_PROXY_HOST); diff --git a/src/test/java/net/snowflake/client/jdbc/diagnostic/SnowflakeEndpointTest.java b/src/test/java/net/snowflake/client/jdbc/diagnostic/SnowflakeEndpointTest.java index a926a649e..6072c3453 100644 --- a/src/test/java/net/snowflake/client/jdbc/diagnostic/SnowflakeEndpointTest.java +++ b/src/test/java/net/snowflake/client/jdbc/diagnostic/SnowflakeEndpointTest.java @@ -1,10 +1,10 @@ package net.snowflake.client.jdbc.diagnostic; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; import java.util.Map; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SnowflakeEndpointTest { @@ -20,9 +20,9 @@ public void shouldDetectPrivateLinkEndpoint() { (host, expectedToBePrivateLink) -> { SnowflakeEndpoint endpoint = new SnowflakeEndpoint("SNOWFLAKE_DEPLOYMENT", host, 443); assertEquals( - String.format("Expecting %s to be private link: %s", host, expectedToBePrivateLink), expectedToBePrivateLink, - endpoint.isPrivateLink()); + endpoint.isPrivateLink(), + String.format("Expecting %s to be private link: %s", host, expectedToBePrivateLink)); }); } } diff --git a/src/test/java/net/snowflake/client/jdbc/structuredtypes/ResultSetStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/structuredtypes/ResultSetStructuredTypesLatestIT.java index 2857634f8..1660ea57d 100644 --- a/src/test/java/net/snowflake/client/jdbc/structuredtypes/ResultSetStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/structuredtypes/ResultSetStructuredTypesLatestIT.java @@ -3,11 +3,11 @@ */ package net.snowflake.client.jdbc.structuredtypes; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; import java.sql.Connection; @@ -24,11 +24,10 @@ import java.time.ZoneId; import java.util.List; import java.util.Map; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; import net.snowflake.client.TestUtil; import net.snowflake.client.ThrowingConsumer; -import net.snowflake.client.category.TestCategoryResultSet; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.structs.SnowflakeObjectTypeFactories; import net.snowflake.client.jdbc.BaseJDBCTest; import net.snowflake.client.jdbc.ResultSetFormatType; @@ -39,34 +38,17 @@ import net.snowflake.client.jdbc.structuredtypes.sqldata.NullableFieldsSqlData; import net.snowflake.client.jdbc.structuredtypes.sqldata.SimpleClass; import net.snowflake.client.jdbc.structuredtypes.sqldata.StringClass; -import org.junit.After; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -@Category(TestCategoryResultSet.class) +import net.snowflake.client.providers.ResultFormatProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +@Tag(TestTags.RESULT_SET) public class ResultSetStructuredTypesLatestIT extends BaseJDBCTest { - - @Parameterized.Parameters(name = "format={0}") - public static Object[][] data() { - return new Object[][] { - {ResultSetFormatType.JSON}, - {ResultSetFormatType.ARROW_WITH_JSON_STRUCTURED_TYPES}, - {ResultSetFormatType.NATIVE_ARROW} - }; - } - - private final ResultSetFormatType queryResultFormat; - - public ResultSetStructuredTypesLatestIT(ResultSetFormatType queryResultFormat) { - this.queryResultFormat = queryResultFormat; - } - - @Before + @BeforeEach public void setup() { SnowflakeObjectTypeFactories.register(StringClass.class, StringClass::new); SnowflakeObjectTypeFactories.register(SimpleClass.class, SimpleClass::new); @@ -74,7 +56,7 @@ public void setup() { SnowflakeObjectTypeFactories.register(NullableFieldsSqlData.class, NullableFieldsSqlData::new); } - @After + @AfterEach public void clean() { SnowflakeObjectTypeFactories.unregister(StringClass.class); SnowflakeObjectTypeFactories.unregister(SimpleClass.class); @@ -82,7 +64,7 @@ public void clean() { SnowflakeObjectTypeFactories.unregister(NullableFieldsSqlData.class); } - public Connection init() throws SQLException { + public Connection init(ResultSetFormatType format) throws SQLException { Connection conn = BaseJDBCTest.getConnection(BaseJDBCTest.DONT_INJECT_SOCKET_TIMEOUT); try (Statement stmt = conn.createStatement()) { stmt.execute("alter session set ENABLE_STRUCTURED_TYPES_IN_CLIENT_RESPONSE = true"); @@ -90,9 +72,9 @@ public Connection init() throws SQLException { stmt.execute("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'"); stmt.execute( "alter session set jdbc_query_result_format = '" - + queryResultFormat.sessionParameterTypeValue + + format.sessionParameterTypeValue + "'"); - if (queryResultFormat == ResultSetFormatType.NATIVE_ARROW) { + if (format == ResultSetFormatType.NATIVE_ARROW) { stmt.execute("alter session set ENABLE_STRUCTURED_TYPES_NATIVE_ARROW_FORMAT = true"); stmt.execute("alter session set FORCE_ENABLE_STRUCTURED_TYPES_NATIVE_ARROW_FORMAT = true"); } @@ -100,20 +82,23 @@ public Connection init() throws SQLException { return conn; } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapStructToObjectWithFactory() throws SQLException { - testMapJson(true); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapStructToObjectWithFactory(ResultSetFormatType format) throws SQLException { + testMapJson(true, format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapStructToObjectWithReflection() throws SQLException { - testMapJson(false); - testMapJson(true); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapStructToObjectWithReflection(ResultSetFormatType format) throws SQLException { + testMapJson(false, format); + testMapJson(true, format); } - private void testMapJson(boolean registerFactory) throws SQLException { + private void testMapJson(boolean registerFactory, ResultSetFormatType format) + throws SQLException { if (registerFactory) { SnowflakeObjectTypeFactories.register(StringClass.class, StringClass::new); } else { @@ -124,25 +109,29 @@ private void testMapJson(boolean registerFactory) throws SQLException { (resultSet) -> { StringClass object = resultSet.getObject(1, StringClass.class); assertEquals("a", object.getString()); - }); + }, + format); SnowflakeObjectTypeFactories.register(StringClass.class, StringClass::new); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapNullStruct() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapNullStruct(ResultSetFormatType format) throws SQLException { withFirstRow( "select null::OBJECT(string VARCHAR)", (resultSet) -> { StringClass object = resultSet.getObject(1, StringClass.class); assertNull(object); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapStructAllTypes() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapStructAllTypes(ResultSetFormatType format) throws SQLException { + try (Connection connection = init(format); Statement statement = connection.createStatement()) { statement.execute("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'"); try (ResultSet resultSet = statement.executeQuery(AllTypesClass.ALL_TYPES_QUERY); ) { @@ -179,7 +168,7 @@ public void testMapStructAllTypes() throws SQLException { assertEquals("b", object.getSimpleClass().getString()); assertEquals(Integer.valueOf(2), object.getSimpleClass().getIntValue()); - if (queryResultFormat == ResultSetFormatType.NATIVE_ARROW) { + if (format == ResultSetFormatType.NATIVE_ARROW) { // Only verify getString for Arrow since JSON representations have difficulties with // floating point toString conversion (3.300000000000000e+00 vs 3.3 in native arrow) String expectedArrowGetStringResult = @@ -190,11 +179,13 @@ public void testMapStructAllTypes() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnStructAsStringIfTypeWasNotIndicated() throws SQLException { - Assume.assumeTrue(queryResultFormat != ResultSetFormatType.NATIVE_ARROW); - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnStructAsStringIfTypeWasNotIndicated(ResultSetFormatType format) + throws SQLException { + Assumptions.assumeTrue(format != ResultSetFormatType.NATIVE_ARROW); + try (Connection connection = init(format); Statement statement = connection.createStatement()) { statement.execute( "alter session set " @@ -237,26 +228,30 @@ public void testReturnStructAsStringIfTypeWasNotIndicated() throws SQLException } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testThrowingGettingObjectIfTypeWasNotIndicatedAndFormatNativeArrow() - throws SQLException { - Assume.assumeTrue(queryResultFormat == ResultSetFormatType.NATIVE_ARROW); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testThrowingGettingObjectIfTypeWasNotIndicatedAndFormatNativeArrow( + ResultSetFormatType format) throws SQLException { + Assumptions.assumeTrue(format == ResultSetFormatType.NATIVE_ARROW); withFirstRow( "select {'string':'a'}::OBJECT(string VARCHAR)", (resultSet) -> { assertThrows(SQLException.class, () -> resultSet.getObject(1)); - }); + }, + format); withFirstRow( "select {'x':{'string':'one'},'y':{'string':'two'},'z':{'string':'three'}}::MAP(VARCHAR, OBJECT(string VARCHAR));", (resultSet) -> { assertThrows(SQLException.class, () -> resultSet.getObject(1, Map.class)); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsArrayOfSqlData() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsArrayOfSqlData(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT({'string':'one'}, {'string':'two'}, {'string':'three'})::ARRAY(OBJECT(string VARCHAR))", (resultSet) -> { @@ -265,12 +260,15 @@ public void testReturnAsArrayOfSqlData() throws SQLException { assertEquals("one", resultArray[0].getString()); assertEquals("two", resultArray[1].getString()); assertEquals("three", resultArray[2].getString()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsArrayOfNullableFieldsInSqlData() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsArrayOfNullableFieldsInSqlData(ResultSetFormatType format) + throws SQLException { withFirstRow( "SELECT OBJECT_CONSTRUCT_KEEP_NULL('string', null, 'nullableIntValue', null, 'nullableLongValue', null, " + "'date', null, 'bd', null, 'bytes', null, 'longValue', null)" @@ -287,13 +285,15 @@ public void testReturnAsArrayOfNullableFieldsInSqlData() throws SQLException { assertNull(result.getBd()); assertNull(result.getBytes()); assertEquals(Long.valueOf(0), result.getLongValue()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnNullsForAllTpesInSqlData() throws SQLException { - try (Connection connection = init(); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnNullsForAllTpesInSqlData(ResultSetFormatType format) throws SQLException { + try (Connection connection = init(format); Statement statement = connection.createStatement()) { statement.execute("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'"); try (ResultSet resultSet = @@ -324,9 +324,10 @@ public void testReturnNullsForAllTpesInSqlData() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsArrayOfString() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsArrayOfString(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT('one', 'two','three')::ARRAY(VARCHAR)", (resultSet) -> { @@ -335,13 +336,15 @@ public void testReturnAsArrayOfString() throws SQLException { assertEquals("one", resultArray[0]); assertEquals("two", resultArray[1]); assertEquals("three", resultArray[2]); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsArrayOfNullableString() throws SQLException { - Assume.assumeTrue(queryResultFormat == ResultSetFormatType.NATIVE_ARROW); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsArrayOfNullableString(ResultSetFormatType format) throws SQLException { + Assumptions.assumeTrue(format == ResultSetFormatType.NATIVE_ARROW); withFirstRow( "SELECT ARRAY_CONSTRUCT('one', 'two', null)::ARRAY(VARCHAR)", (resultSet) -> { @@ -350,24 +353,28 @@ public void testReturnAsArrayOfNullableString() throws SQLException { assertEquals("one", resultArray[0]); assertEquals("two", resultArray[1]); assertNull(resultArray[2]); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnNullAsArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnNullAsArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT null::ARRAY(VARCHAR)", (resultSet) -> { String[] resultArray = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, String.class); assertNull(resultArray); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsListOfIntegers() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsListOfIntegers(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(1,2,3)::ARRAY(INTEGER)", (resultSet) -> { @@ -376,12 +383,14 @@ public void testReturnAsListOfIntegers() throws SQLException { assertEquals(Integer.valueOf(1), resultList.get(0)); assertEquals(Integer.valueOf(2), resultList.get(1)); assertEquals(Integer.valueOf(3), resultList.get(2)); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsListOfFloat() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsListOfFloat(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(1.1,2.2,3.3)::ARRAY(FLOAT)", (resultSet) -> { @@ -390,12 +399,14 @@ public void testReturnAsListOfFloat() throws SQLException { assertEquals(Float.valueOf(1.1f), resultList[0]); assertEquals(Float.valueOf(2.2f), resultList[1]); assertEquals(Float.valueOf(3.3f), resultList[2]); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsListOfDouble() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsListOfDouble(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(1.1,2.2,3.3)::ARRAY(DOUBLE)", (resultSet) -> { @@ -404,12 +415,14 @@ public void testReturnAsListOfDouble() throws SQLException { assertEquals(Double.valueOf(1.1), resultList.get(0)); assertEquals(Double.valueOf(2.2), resultList.get(1)); assertEquals(Double.valueOf(3.3), resultList.get(2)); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMap() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMap(ResultSetFormatType format) throws SQLException { withFirstRow( "select {'x':{'string':'one'},'y':{'string':'two'},'z':{'string':'three'}}::MAP(VARCHAR, OBJECT(string VARCHAR));", (resultSet) -> { @@ -418,13 +431,15 @@ public void testReturnAsMap() throws SQLException { assertEquals("one", map.get("x").getString()); assertEquals("two", map.get("y").getString()); assertEquals("three", map.get("z").getString()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMapByGetObject() throws SQLException { - Assume.assumeTrue(queryResultFormat != ResultSetFormatType.NATIVE_ARROW); + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMapByGetObject(ResultSetFormatType format) throws SQLException { + Assumptions.assumeTrue(format != ResultSetFormatType.NATIVE_ARROW); withFirstRow( "select {'x':{'string':'one'},'y':{'string':'two'},'z':{'string':'three'}}::MAP(VARCHAR, OBJECT(string VARCHAR));", (resultSet) -> { @@ -432,12 +447,14 @@ public void testReturnAsMapByGetObject() throws SQLException { assertEquals("one", map.get("x").get("string")); assertEquals("two", map.get("y").get("string")); assertEquals("three", map.get("z").get("string")); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMapWithNullableValues() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMapWithNullableValues(ResultSetFormatType format) throws SQLException { withFirstRow( "select {'x':{'string':'one'},'y':null,'z':{'string':'three'}}::MAP(VARCHAR, OBJECT(string VARCHAR));", (resultSet) -> { @@ -446,36 +463,42 @@ public void testReturnAsMapWithNullableValues() throws SQLException { assertEquals("one", map.get("x").getString()); assertNull(map.get("y")); assertEquals("three", map.get("z").getString()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnNullAsObjectOfTypeMap() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnNullAsObjectOfTypeMap(ResultSetFormatType format) throws SQLException { withFirstRow( "select null::MAP(VARCHAR, OBJECT(string VARCHAR));", (resultSet) -> { Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getObject(1, Map.class); assertNull(map); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnNullAsMap() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnNullAsMap(ResultSetFormatType format) throws SQLException { withFirstRow( "select null::MAP(VARCHAR, OBJECT(string VARCHAR));", (resultSet) -> { Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, StringClass.class); assertNull(map); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMapOfTimestampsNtz() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMapOfTimestampsNtz(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT {'x': TO_TIMESTAMP_NTZ('2021-12-23 09:44:44'), 'y': TO_TIMESTAMP_NTZ('2021-12-24 09:55:55')}::MAP(VARCHAR, TIMESTAMP)", (resultSet) -> { @@ -491,12 +514,14 @@ public void testReturnAsMapOfTimestampsNtz() throws SQLException { .atZone(ZoneId.of("Europe/Warsaw")) .toInstant(), map.get("y").toInstant()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMapOfTimestampsLtz() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMapOfTimestampsLtz(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT {'x': TO_TIMESTAMP_LTZ('2021-12-23 09:44:44'), 'y': TO_TIMESTAMP_LTZ('2021-12-24 09:55:55')}::MAP(VARCHAR, TIMESTAMP_LTZ)", (resultSet) -> { @@ -512,12 +537,14 @@ public void testReturnAsMapOfTimestampsLtz() throws SQLException { .atZone(ZoneId.of("Europe/Warsaw")) .toInstant(), map.get("y").toInstant()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMapOfLong() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMapOfLong(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT {'x':1, 'y':2, 'z':3}::MAP(VARCHAR, BIGINT)", (resultSet) -> { @@ -526,12 +553,14 @@ public void testReturnAsMapOfLong() throws SQLException { assertEquals(Long.valueOf(1), map.get("x")); assertEquals(Long.valueOf(2), map.get("y")); assertEquals(Long.valueOf(3), map.get("z")); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMapOfDate() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMapOfDate(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT {'x':'2023-12-24', 'y':'2023-12-25'}::MAP(VARCHAR, DATE)", (resultSet) -> { @@ -541,12 +570,14 @@ public void testReturnAsMapOfDate() throws SQLException { Date.valueOf(LocalDate.of(2023, 12, 24)).toString(), map.get("x").toString()); assertEquals( Date.valueOf(LocalDate.of(2023, 12, 25)).toString(), map.get("y").toString()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMapOfTime() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMapOfTime(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT {'x':'12:34:56', 'y':'12:34:58'}::MAP(VARCHAR, TIME)", (resultSet) -> { @@ -554,12 +585,14 @@ public void testReturnAsMapOfTime() throws SQLException { resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Time.class); assertEquals(Time.valueOf(LocalTime.of(12, 34, 56)), map.get("x")); assertEquals(Time.valueOf(LocalTime.of(12, 34, 58)), map.get("y")); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsMapOfBoolean() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsMapOfBoolean(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT {'x':'true', 'y':0}::MAP(VARCHAR, BOOLEAN)", (resultSet) -> { @@ -567,12 +600,14 @@ public void testReturnAsMapOfBoolean() throws SQLException { resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Boolean.class); assertEquals(Boolean.TRUE, map.get("x")); assertEquals(Boolean.FALSE, map.get("y")); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testReturnAsList() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testReturnAsList(ResultSetFormatType format) throws SQLException { withFirstRow( "select [{'string':'one'},{'string': 'two'}]::ARRAY(OBJECT(string varchar))", (resultSet) -> { @@ -580,12 +615,14 @@ public void testReturnAsList() throws SQLException { resultSet.unwrap(SnowflakeBaseResultSet.class).getList(1, StringClass.class); assertEquals("one", map.get(0).getString()); assertEquals("two", map.get(1).getString()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapStructsFromChunks() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapStructsFromChunks(ResultSetFormatType format) throws SQLException { withFirstRow( "select {'string':'a'}::OBJECT(string VARCHAR) FROM TABLE(GENERATOR(ROWCOUNT=>30000))", (resultSet) -> { @@ -593,12 +630,14 @@ public void testMapStructsFromChunks() throws SQLException { StringClass object = resultSet.getObject(1, StringClass.class); assertEquals("a", object.getString()); } - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapIntegerArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapIntegerArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(10, 20, 30)::ARRAY(INTEGER)", (resultSet) -> { @@ -606,12 +645,14 @@ public void testMapIntegerArray() throws SQLException { assertEquals(Long.valueOf(10), resultArray[0]); assertEquals(Long.valueOf(20), resultArray[1]); assertEquals(Long.valueOf(30), resultArray[2]); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapFixedToLongArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapFixedToLongArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(10, 20, 30)::ARRAY(SMALLINT)", (resultSet) -> { @@ -619,14 +660,16 @@ public void testMapFixedToLongArray() throws SQLException { assertEquals(Long.valueOf("10"), resultArray[0]); assertEquals(Long.valueOf("20"), resultArray[1]); assertEquals(Long.valueOf("30"), resultArray[2]); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapDecimalArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapDecimalArray(ResultSetFormatType format) throws SQLException { // when: jdbc_treat_decimal_as_int=true scale=0 - try (Connection connection = init(); + try (Connection connection = init(format); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( @@ -639,7 +682,7 @@ public void testMapDecimalArray() throws SQLException { } // when: jdbc_treat_decimal_as_int=true scale=2 - try (Connection connection = init(); + try (Connection connection = init(format); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery( @@ -652,7 +695,7 @@ public void testMapDecimalArray() throws SQLException { } // when: jdbc_treat_decimal_as_int=false scale=0 - try (Connection connection = init(); + try (Connection connection = init(format); Statement statement = connection.createStatement(); ) { statement.execute("alter session set jdbc_treat_decimal_as_int = false"); try (ResultSet resultSet = @@ -666,9 +709,10 @@ public void testMapDecimalArray() throws SQLException { } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapVarcharArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapVarcharArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT 'text', ARRAY_CONSTRUCT('10', '20','30')::ARRAY(VARCHAR)", (resultSet) -> { @@ -677,12 +721,14 @@ public void testMapVarcharArray() throws SQLException { assertEquals("10", resultArray[0]); assertEquals("20", resultArray[1]); assertEquals("30", resultArray[2]); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapDatesArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapDatesArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(to_date('2023-12-24', 'YYYY-MM-DD'), to_date('2023-12-25', 'YYYY-MM-DD'))::ARRAY(DATE)", (resultSet) -> { @@ -691,12 +737,14 @@ public void testMapDatesArray() throws SQLException { Date.valueOf(LocalDate.of(2023, 12, 24)).toString(), resultArray[0].toString()); assertEquals( Date.valueOf(LocalDate.of(2023, 12, 25)).toString(), resultArray[1].toString()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapTimeArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapTimeArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(to_time('15:39:20.123'), to_time('09:12:20.123'))::ARRAY(TIME)", (resultSet) -> { @@ -704,12 +752,14 @@ public void testMapTimeArray() throws SQLException { assertEquals( Time.valueOf(LocalTime.of(15, 39, 20)).toString(), resultArray[0].toString()); assertEquals(Time.valueOf(LocalTime.of(9, 12, 20)).toString(), resultArray[1].toString()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapTimestampArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapTimestampArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(TO_TIMESTAMP_NTZ('2021-12-23 09:44:44'), TO_TIMESTAMP_NTZ('2021-12-24 09:55:55'))::ARRAY(TIMESTAMP)", (resultSet) -> { @@ -724,36 +774,42 @@ public void testMapTimestampArray() throws SQLException { .atZone(ZoneId.of("Europe/Warsaw")) .toInstant(), resultArray[1].toInstant()); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapBooleanArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapBooleanArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(true,false)::ARRAY(BOOLEAN)", (resultSet) -> { Boolean[] resultArray = (Boolean[]) resultSet.getArray(1).getArray(); assertEquals(true, resultArray[0]); assertEquals(false, resultArray[1]); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapBinaryArray() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapBinaryArray(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(TO_BINARY('616263', 'HEX'),TO_BINARY('616263', 'HEX'))::ARRAY(BINARY)", (resultSet) -> { Byte[][] resultArray = (Byte[][]) resultSet.getArray(1).getArray(); assertArrayEquals(new Byte[] {'a', 'b', 'c'}, resultArray[0]); assertArrayEquals(new Byte[] {'a', 'b', 'c'}, resultArray[1]); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapArrayOfStructToMap() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapArrayOfStructToMap(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT({'x': 'abc', 'y': 1}, {'x': 'def', 'y': 2} )::ARRAY(OBJECT(x VARCHAR, y INTEGER))", (resultSet) -> { @@ -764,12 +820,14 @@ public void testMapArrayOfStructToMap() throws SQLException { assertEquals(firstEntry.get("y").toString(), "1"); assertEquals(secondEntry.get("x").toString(), "def"); assertEquals(secondEntry.get("y").toString(), "2"); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapArrayOfArrays() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapArrayOfArrays(ResultSetFormatType format) throws SQLException { withFirstRow( "SELECT ARRAY_CONSTRUCT(ARRAY_CONSTRUCT({'x': 'abc', 'y': 1}, {'x': 'def', 'y': 2}) )::ARRAY(ARRAY(OBJECT(x VARCHAR, y INTEGER)))", (resultSet) -> { @@ -780,12 +838,14 @@ public void testMapArrayOfArrays() throws SQLException { assertEquals(firstEntry.get("y").toString(), "1"); assertEquals(secondEntry.get("x").toString(), "def"); assertEquals(secondEntry.get("y").toString(), "2"); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testMapNestedStructures() throws SQLException { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testMapNestedStructures(ResultSetFormatType format) throws SQLException { String structSelectStatement = "SELECT {'simpleClass': {'string': 'a', 'intValue': 2}, " + "'simpleClasses': ARRAY_CONSTRUCT({'string': 'a', 'intValue': 2}, {'string': 'b', 'intValue': 2}), " @@ -850,22 +910,28 @@ public void testMapNestedStructures() throws SQLException { assertEquals(Integer.valueOf(3), nestedStructSqlData.getMapOfIntegers().get("x")); assertEquals(Integer.valueOf(4), nestedStructSqlData.getMapOfIntegers().get("y")); TestUtil.assertEqualsIgnoringWhitespace(expectedQueryResult, resultSet.getString(1)); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testColumnTypeWhenStructureTypeIsDisabled() throws Exception { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testColumnTypeWhenStructureTypeIsDisabled(ResultSetFormatType format) + throws Exception { withFirstRow( "SELECT {'string':'a'}", resultSet -> { assertEquals(Types.VARCHAR, resultSet.getMetaData().getColumnType(1)); - }); + }, + format); } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testColumnTypeAndFieldsWhenStructureTypeIsReturned() throws Exception { + @ParameterizedTest + @ArgumentsSource(ResultFormatProvider.class) + @DontRunOnGithubActions + public void testColumnTypeAndFieldsWhenStructureTypeIsReturned(ResultSetFormatType format) + throws Exception { withFirstRow( "SELECT {'string':'a'}::OBJECT(string VARCHAR)", resultSet -> { @@ -893,12 +959,16 @@ public void testColumnTypeAndFieldsWhenStructureTypeIsReturned() throws Exceptio .getColumnFields(1) .get(0) .getName()); - }); + }, + format); } - private void withFirstRow(String sqlText, ThrowingConsumer consumer) + private void withFirstRow( + String sqlText, + ThrowingConsumer consumer, + ResultSetFormatType format) throws SQLException { - try (Connection connection = init(); + try (Connection connection = init(format); Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(sqlText); ) { assertTrue(rs.next()); diff --git a/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java index 352d2b1a4..c3ae5fdd8 100644 --- a/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java +++ b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringArrowJsonCompatibilityIT.java @@ -2,137 +2,161 @@ import java.sql.Connection; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryResultSet; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.ResultSetFormatType; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import net.snowflake.client.providers.ProvidersUtil; +import net.snowflake.client.providers.ResultFormatProvider; +import net.snowflake.client.providers.SnowflakeArgumentsProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsSource; -@RunWith(Parameterized.class) -@Category(TestCategoryResultSet.class) +@Tag(TestTags.RESULT_SET) public class StructuredTypesGetStringArrowJsonCompatibilityIT extends StructuredTypesGetStringBaseIT { - - private final String expectedStructureTypeRepresentation; - private final String selectSql; private static Map connections = new HashMap<>(); - public StructuredTypesGetStringArrowJsonCompatibilityIT( - ResultSetFormatType queryResultFormat, - String selectSql, - String expectedStructureTypeRepresentation) { - super(queryResultFormat); - this.selectSql = selectSql; - this.expectedStructureTypeRepresentation = expectedStructureTypeRepresentation; - } - - @Before - public void setUpConnection() throws SQLException { + @BeforeAll + public static void setUpConnections() throws SQLException { // We initialize connection here since we need to set server properties that cannot be set in GH // actions and before class is running even when all the tests have conditional ignore of tests - Connection connection = connections.get(queryResultFormat); - if (connection == null) { + for (ResultSetFormatType queryResultFormat : ResultSetFormatType.values()) { connections.put(queryResultFormat, initConnection(queryResultFormat)); } } - @AfterClass + @AfterAll public static void closeConnections() throws SQLException { for (Connection connection : connections.values()) { connection.close(); } } - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testRunAsGetString() throws SQLException { + @ParameterizedTest + @DontRunOnGithubActions + @ArgumentsSource(DataProvider.class) + public void testRunAsGetString( + ResultSetFormatType queryResultFormat, + String selectSql, + String expectedStructureTypeRepresentation) + throws SQLException { withFirstRow( connections.get(queryResultFormat), selectSql, (resultSet) -> assertGetStringIsCompatible(resultSet, expectedStructureTypeRepresentation)); } - @Parameterized.Parameters(name = "format={0},sql={1}") - public static Collection data() { - Map samples = new LinkedHashMap<>(); - samples.put("select {'a':3}::map(text, int);", "{\"a\":3}"); - samples.put( - "select {'a':'zażółć gęślą jaźń'}::map(text, text);", "{\"a\":\"zażółć gęślą jaźń\"}"); - samples.put("select {'a':'bla'}::map(text, text);", "{\"a\":\"bla\"}"); - samples.put("select {'1':'bla'}::map(int, text);", "{\"1\":\"bla\"}"); - samples.put("select {'1':[1,2,3]}::map(int, ARRAY(int));", "{\"1\":[1,2,3]}"); - samples.put( - "select {'1':{'string':'a'}}::map(int, OBJECT(string VARCHAR));", - "{\"1\":{\"string\":\"a\"}}"); - samples.put( - "select {'1':{'string':'a'}}::map(int, map(string, string));", - "{\"1\":{\"string\":\"a\"}}"); - samples.put( - "select {'1':[{'string':'a'},{'bla':'ble'}]}::map(int, array(map(string, string)));", - "{\"1\":[{\"string\":\"a\"},{\"bla\":\"ble\"}]}"); - samples.put("select [1,2,3]::array(int)", "[1,2,3]"); - samples.put( - "select [{'a':'a'}, {'b':'b'}]::array(map(string, string))", - "[{\"a\":\"a\"}, {\"b\":\"b\"}]"); - samples.put( - "select [{'a':true}, {'b':false}]::array(map(string, boolean))", - "[{\"a\":true}, {\"b\":false}]"); - samples.put( - "select [{'string':'a'}, {'string':'b'}]::array(object(string varchar))", - "[{\"string\":\"a\"}, {\"string\":\"b\"}]"); - samples.put("select {'string':'a'}::object(string varchar)", "{\"string\":\"a\"}"); - samples.put( - "select {'x':'a','b':'a','c':'a','d':'a','e':'a'}::object(x varchar,b varchar,c varchar,d varchar,e varchar)", - "{\"x\":\"a\",\"b\":\"a\",\"c\":\"a\",\"d\":\"a\",\"e\":\"a\"}"); - samples.put("select {'string':[1,2,3]}::object(string array(int))", "{\"string\":[1,2,3]}"); - samples.put( - "select {'string':{'a':15}}::object(string object(a int))", "{\"string\":{\"a\":15}}"); - samples.put( - "select {'string':{'a':15}}::object(string map(string,int))", "{\"string\":{\"a\":15}}"); - samples.put( - "select {'string':{'a':{'b':15}}}::object(string object(a map(string, int)))", - "{\"string\":{\"a\":{\"b\":15}}}"); + public static class SampleProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + List samples = new LinkedList<>(); + samples.add(Arguments.of("select {'a':3}::map(text, int);", "{\"a\":3}")); + samples.add( + Arguments.of( + "select {'a':'zażółć gęślą jaźń'}::map(text, text);", + "{\"a\":\"zażółć gęślą jaźń\"}")); + samples.add(Arguments.of("select {'a':'bla'}::map(text, text);", "{\"a\":\"bla\"}")); + samples.add(Arguments.of("select {'1':'bla'}::map(int, text);", "{\"1\":\"bla\"}")); + samples.add(Arguments.of("select {'1':[1,2,3]}::map(int, ARRAY(int));", "{\"1\":[1,2,3]}")); + samples.add( + Arguments.of( + "select {'1':{'string':'a'}}::map(int, OBJECT(string VARCHAR));", + "{\"1\":{\"string\":\"a\"}}")); + samples.add( + Arguments.of( + "select {'1':{'string':'a'}}::map(int, map(string, string));", + "{\"1\":{\"string\":\"a\"}}")); + samples.add( + Arguments.of( + "select {'1':[{'string':'a'},{'bla':'ble'}]}::map(int, array(map(string, string)));", + "{\"1\":[{\"string\":\"a\"},{\"bla\":\"ble\"}]}")); + samples.add(Arguments.of("select [1,2,3]::array(int)", "[1,2,3]")); + samples.add( + Arguments.of( + "select [{'a':'a'}, {'b':'b'}]::array(map(string, string))", + "[{\"a\":\"a\"}, {\"b\":\"b\"}]")); + samples.add( + Arguments.of( + "select [{'a':true}, {'b':false}]::array(map(string, boolean))", + "[{\"a\":true}, {\"b\":false}]")); + samples.add( + Arguments.of( + "select [{'string':'a'}, {'string':'b'}]::array(object(string varchar))", + "[{\"string\":\"a\"}, {\"string\":\"b\"}]")); + samples.add( + Arguments.of("select {'string':'a'}::object(string varchar)", "{\"string\":\"a\"}")); + samples.add( + Arguments.of( + "select {'x':'a','b':'a','c':'a','d':'a','e':'a'}::object(x varchar,b varchar,c varchar,d varchar,e varchar)", + "{\"x\":\"a\",\"b\":\"a\",\"c\":\"a\",\"d\":\"a\",\"e\":\"a\"}")); + samples.add( + Arguments.of( + "select {'string':[1,2,3]}::object(string array(int))", "{\"string\":[1,2,3]}")); + samples.add( + Arguments.of( + "select {'string':{'a':15}}::object(string object(a int))", + "{\"string\":{\"a\":15}}")); + samples.add( + Arguments.of( + "select {'string':{'a':15}}::object(string map(string,int))", + "{\"string\":{\"a\":15}}")); + samples.add( + Arguments.of( + "select {'string':{'a':{'b':15}}}::object(string object(a map(string, int)))", + "{\"string\":{\"a\":{\"b\":15}}}")); - samples.put( - "select {'string':{'a':{'b':[{'c': 15}]}}}::object(string map(string, object(b array(object(c int)))))", - "{\"string\":{\"a\":{\"b\":[{\"c\":15}]}}}"); - // DY, DD MON YYYY HH24:MI:SS TZHTZM - samples.put( - "select {'ltz': '2024-05-20 11:22:33'::TIMESTAMP_LTZ}::object(ltz TIMESTAMP_LTZ)", - "{\"ltz\":\"Mon, 20 May 2024 11:22:33 +0200\"}"); - samples.put( - "select {'ntz': '2024-05-20 11:22:33'::TIMESTAMP_NTZ}::object(ntz TIMESTAMP_NTZ)", - "{\"ntz\":\"Mon, 20 May 2024 11:22:33 Z\"}"); - samples.put( - "select {'tz': '2024-05-20 11:22:33+0800'::TIMESTAMP_TZ}::object(tz TIMESTAMP_TZ)", - "{\"tz\":\"Mon, 20 May 2024 11:22:33 +0800\"}"); - samples.put( - "select {'date': '2024-05-20'::DATE}::object(date DATE)", "{\"date\":\"2024-05-20\"}"); - samples.put("select {'time': '22:14:55'::TIME}::object(time TIME)", "{\"time\":\"22:14:55\"}"); - samples.put("select {'bool': TRUE}::object(bool BOOLEAN)", "{\"bool\":true}"); - samples.put("select {'bool': 'y'}::object(bool BOOLEAN)", "{\"bool\":true}"); - samples.put( - "select {'binary': TO_BINARY('616263', 'HEX')}::object(binary BINARY)", - "{\"binary\":\"616263\"}"); - samples.put("select [1,2,3]::VECTOR(INT, 3)", "[1,2,3]"); - samples.put("select ['a','b','c']::ARRAY(varchar)", "[\"a\",\"b\",\"c\"]"); + samples.add( + Arguments.of( + "select {'string':{'a':{'b':[{'c': 15}]}}}::object(string map(string, object(b array(object(c int)))))", + "{\"string\":{\"a\":{\"b\":[{\"c\":15}]}}}")); + // DY, DD MON YYYY HH24:MI:SS TZHTZM + samples.add( + Arguments.of( + "select {'ltz': '2024-05-20 11:22:33'::TIMESTAMP_LTZ}::object(ltz TIMESTAMP_LTZ)", + "{\"ltz\":\"Mon, 20 May 2024 11:22:33 +0200\"}")); + samples.add( + Arguments.of( + "select {'ntz': '2024-05-20 11:22:33'::TIMESTAMP_NTZ}::object(ntz TIMESTAMP_NTZ)", + "{\"ntz\":\"Mon, 20 May 2024 11:22:33 Z\"}")); + samples.add( + Arguments.of( + "select {'tz': '2024-05-20 11:22:33+0800'::TIMESTAMP_TZ}::object(tz TIMESTAMP_TZ)", + "{\"tz\":\"Mon, 20 May 2024 11:22:33 +0800\"}")); + samples.add( + Arguments.of( + "select {'date': '2024-05-20'::DATE}::object(date DATE)", + "{\"date\":\"2024-05-20\"}")); + samples.add( + Arguments.of( + "select {'time': '22:14:55'::TIME}::object(time TIME)", "{\"time\":\"22:14:55\"}")); + samples.add(Arguments.of("select {'bool': TRUE}::object(bool BOOLEAN)", "{\"bool\":true}")); + samples.add(Arguments.of("select {'bool': 'y'}::object(bool BOOLEAN)", "{\"bool\":true}")); + samples.add( + Arguments.of( + "select {'binary': TO_BINARY('616263', 'HEX')}::object(binary BINARY)", + "{\"binary\":\"616263\"}")); + samples.add(Arguments.of("select [1,2,3]::VECTOR(INT, 3)", "[1,2,3]")); + samples.add(Arguments.of("select ['a','b','c']::ARRAY(varchar)", "[\"a\",\"b\",\"c\"]")); - Collection parameters = new ArrayList<>(); - for (ResultSetFormatType resultSetFormatType : ResultSetFormatType.values()) { - samples.forEach( - (sql, expected) -> parameters.add(new Object[] {resultSetFormatType, sql, expected})); + return samples; } + } - return parameters; + private static class DataProvider extends SnowflakeArgumentsProvider { + + @Override + protected List rawArguments(ExtensionContext context) { + return ProvidersUtil.cartesianProduct( + context, new ResultFormatProvider(), new SampleProvider()); + } } } diff --git a/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringBaseIT.java b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringBaseIT.java index d9d5c15e2..35d10c4b1 100644 --- a/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringBaseIT.java +++ b/src/test/java/net/snowflake/client/jdbc/structuredtypes/StructuredTypesGetStringBaseIT.java @@ -1,6 +1,6 @@ package net.snowflake.client.jdbc.structuredtypes; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.Connection; import java.sql.ResultSet; @@ -12,15 +12,10 @@ import net.snowflake.client.jdbc.ResultSetFormatType; abstract class StructuredTypesGetStringBaseIT extends BaseJDBCTest { + public StructuredTypesGetStringBaseIT() {} - protected final ResultSetFormatType queryResultFormat; - - public StructuredTypesGetStringBaseIT(ResultSetFormatType queryResultFormat) { - this.queryResultFormat = queryResultFormat; - } - - protected Connection init() throws SQLException { - return initConnection(this.queryResultFormat); + protected Connection init(ResultSetFormatType queryResultFormat) throws SQLException { + return initConnection(queryResultFormat); } protected static Connection initConnection(ResultSetFormatType queryResultFormat) diff --git a/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryIT.java b/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryIT.java index e100534e7..302146801 100644 --- a/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryIT.java +++ b/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryIT.java @@ -3,9 +3,9 @@ */ package net.snowflake.client.jdbc.telemetry; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -18,25 +18,23 @@ import java.sql.Statement; import java.util.Map; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.annotations.DontRunOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.HttpUtil; import net.snowflake.client.core.SFException; import net.snowflake.client.core.SessionUtil; import org.apache.http.impl.client.CloseableHttpClient; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class TelemetryIT extends AbstractDriverIT { private Connection connection = null; private static final ObjectMapper mapper = new ObjectMapper(); - @Before + @BeforeEach public void init() throws SQLException, IOException { this.connection = getConnection(); } @@ -47,23 +45,23 @@ public void testTelemetry() throws Exception { testTelemetryInternal(telemetry); } - @Ignore + @Disabled @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testSessionlessTelemetry() throws Exception, SFException { testTelemetryInternal(createSessionlessTelemetry()); } - @Ignore + @Disabled @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testJWTSessionlessTelemetry() throws Exception, SFException { testTelemetryInternal(createJWTSessionlessTelemetry()); } - @Ignore + @Disabled @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testOAuthSessionlessTelemetry() throws Exception, SFException { testTelemetryInternal(createOAuthSessionlessTelemetry()); } @@ -143,13 +141,13 @@ public void testDisableTelemetry() throws Exception { } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDisableJWTSessionlessTelemetry() throws Exception, SFException { testDisableTelemetryInternal(createJWTSessionlessTelemetry()); } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testDisableOAuthSessionlessTelemetry() throws Exception, SFException { testDisableTelemetryInternal(createOAuthSessionlessTelemetry()); } @@ -181,7 +179,7 @@ public void testDisableTelemetryInternal(TelemetryClient telemetry) throws Excep } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testClosedJWTSessionlessTelemetry() throws Exception, SFException { TelemetryClient telemetry = createJWTSessionlessTelemetry(); telemetry.close(); @@ -189,11 +187,11 @@ public void testClosedJWTSessionlessTelemetry() throws Exception, SFException { node.put("type", "query"); node.put("query_id", "sdasdasdasdasds"); telemetry.addLogToBatch(node, 1234567); - Assert.assertFalse(telemetry.sendBatchAsync().get()); + assertFalse(telemetry.sendBatchAsync().get()); } @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + @DontRunOnGithubActions public void testClosedOAuthSessionlessTelemetry() throws Exception, SFException { TelemetryClient telemetry = createOAuthSessionlessTelemetry(); telemetry.close(); @@ -201,7 +199,7 @@ public void testClosedOAuthSessionlessTelemetry() throws Exception, SFException node.put("type", "query"); node.put("query_id", "sdasdasdasdasds"); telemetry.addLogToBatch(node, 1234567); - Assert.assertFalse(telemetry.sendBatchAsync().get()); + assertFalse(telemetry.sendBatchAsync().get()); } // Helper function to create a sessionless telemetry diff --git a/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryTest.java b/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryTest.java index 6fc0c86d6..3d1471eb7 100644 --- a/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryTest.java +++ b/src/test/java/net/snowflake/client/jdbc/telemetry/TelemetryTest.java @@ -3,13 +3,13 @@ */ package net.snowflake.client.jdbc.telemetry; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.LinkedList; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** Telemetry unit tests */ public class TelemetryTest { diff --git a/src/test/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryServiceIT.java b/src/test/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryServiceIT.java index 1d8ec8c9e..347bc97e3 100644 --- a/src/test/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryServiceIT.java +++ b/src/test/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryServiceIT.java @@ -2,9 +2,10 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; import java.sql.SQLException; @@ -13,9 +14,8 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningNotOnTestaccount; -import net.snowflake.client.category.TestCategoryCore; +import net.snowflake.client.annotations.RunOnTestaccountNotOnGithubActions; +import net.snowflake.client.category.TestTags; import net.snowflake.client.core.SFSession; import net.snowflake.client.jdbc.BaseJDBCTest; import net.snowflake.client.jdbc.SnowflakeConnectionV1; @@ -23,19 +23,19 @@ import net.snowflake.client.jdbc.SnowflakeSQLLoggedException; import net.snowflake.common.core.SqlState; import org.apache.commons.lang3.time.StopWatch; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Standalone test cases for the out of band telemetry service */ -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class TelemetryServiceIT extends BaseJDBCTest { private static final int WAIT_FOR_TELEMETRY_REPORT_IN_MILLISECS = 5000; private boolean defaultState; - @Before + @BeforeEach public void setUp() { TelemetryService service = TelemetryService.getInstance(); Map connectionParams = getConnectionParameters(); @@ -45,7 +45,7 @@ public void setUp() { service.enable(); } - @After + @AfterEach public void tearDown() throws InterruptedException { // wait 5 seconds while the service is flushing TimeUnit.SECONDS.sleep(5); @@ -58,7 +58,7 @@ public void tearDown() throws InterruptedException { } @SuppressWarnings("divzero") - @Ignore + @Disabled @Test public void testCreateException() { TelemetryService service = TelemetryService.getInstance(); @@ -82,7 +82,7 @@ public void testCreateException() { } /** test wrong server url. */ - @Ignore + @Disabled @Test public void testWrongServerURL() throws InterruptedException { TelemetryService service = TelemetryService.getInstance(); @@ -102,7 +102,7 @@ public void testWrongServerURL() throws InterruptedException { assertThat("WrongServerURL do not block.", service.getEventCount() > count); } - @Ignore + @Disabled @Test public void testCreateLog() { // this log will be delivered to snowflake @@ -114,7 +114,7 @@ public void testCreateLog() { service.report(log); } - @Ignore + @Disabled @Test public void testCreateLogWithAWSSecret() { // this log will be delivered to snowflake @@ -135,7 +135,7 @@ public void testCreateLogWithAWSSecret() { service.report(log); } - @Ignore + @Disabled @Test public void stressTestCreateLog() { // this log will be delivered to snowflake @@ -161,7 +161,7 @@ public void stressTestCreateLog() { sw.stop(); } - @Ignore + @Disabled @Test public void testCreateLogInBlackList() { // this log will be delivered to snowflake @@ -172,7 +172,7 @@ public void testCreateLogInBlackList() { service.report(log); } - @Ignore + @Disabled @Test public void testCreateUrgentEvent() { // this log will be delivered to snowflake @@ -184,7 +184,7 @@ public void testCreateUrgentEvent() { service.report(log); } - @Ignore + @Disabled @Test public void stressTestCreateUrgentEvent() { // this log will be delivered to snowflake @@ -229,7 +229,7 @@ private int generateSQLFeatureNotSupportedException() throws SQLFeatureNotSuppor * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningNotOnTestaccount.class) + @RunOnTestaccountNotOnGithubActions public void testSnowflakeSQLLoggedExceptionOOBTelemetry() throws SQLException, InterruptedException { // make a connection to initialize telemetry instance @@ -264,7 +264,7 @@ public void testSnowflakeSQLLoggedExceptionOOBTelemetry() * @throws SQLException */ @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningNotOnTestaccount.class) + @RunOnTestaccountNotOnGithubActions public void testSQLFeatureNotSupportedOOBTelemetry() throws InterruptedException { // with null session, OOB telemetry will be thrown try { @@ -290,7 +290,7 @@ public void testSQLFeatureNotSupportedOOBTelemetry() throws InterruptedException * * @throws SQLException */ - @Ignore + @Disabled @Test public void testHTAPTelemetry() throws SQLException { Properties properties = new Properties(); @@ -317,7 +317,7 @@ public void testHTAPTelemetry() throws SQLException { * Requires part 2 of SNOW-844477. Make sure CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED is true at * account level. Tests connection property CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED=true */ - @Ignore + @Disabled @Test public void testOOBTelemetryEnabled() throws SQLException { Properties properties = new Properties(); @@ -334,7 +334,7 @@ public void testOOBTelemetryEnabled() throws SQLException { * Requires part 2 of SNOW-844477. Make sure CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED is false at * account level. Tests connection property CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED=false */ - @Ignore + @Disabled @Test public void testOOBTelemetryDisabled() throws SQLException { Properties properties = new Properties(); @@ -352,7 +352,7 @@ public void testOOBTelemetryDisabled() throws SQLException { * account level. Tests connection property CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED=false but * CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED is enabled on account level */ - @Ignore + @Disabled @Test public void testOOBTelemetryEnabledOnServerDisabledOnClient() throws SQLException { Properties properties = new Properties(); @@ -392,16 +392,15 @@ public void testSnowflakeSQLLoggedExceptionIBTelemetry() throws SQLException { * telemetry should be used. * *

After running test, check for telemetry message in client_telemetry_v table. - * - * @throws SQLException */ - @Test(expected = SQLFeatureNotSupportedException.class) + @Test public void testSqlFeatureNotSupportedExceptionIBTelemetry() throws SQLException { // make a connection to initialize telemetry instance try (Connection con = getConnection()) { Statement statement = con.createStatement(); // try to execute a statement that throws a SQLFeatureNotSupportedException - statement.execute("select 1", new int[] {}); + assertThrows( + SQLFeatureNotSupportedException.class, () -> statement.execute("select 1", new int[] {})); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryServiceTest.java b/src/test/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryServiceTest.java index 5103348fa..fe359b0ec 100644 --- a/src/test/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryServiceTest.java +++ b/src/test/java/net/snowflake/client/jdbc/telemetryOOB/TelemetryServiceTest.java @@ -6,21 +6,21 @@ import java.util.HashMap; import java.util.Map; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class TelemetryServiceTest { private boolean defaultState; - @Before + @BeforeEach public void setUp() { TelemetryService service = TelemetryService.getInstance(); defaultState = service.isEnabled(); service.enable(); } - @After + @AfterEach public void tearDown() throws InterruptedException { TelemetryService service = TelemetryService.getInstance(); if (defaultState) { diff --git a/src/test/java/net/snowflake/client/loader/FlatfileReadMultithreadIT.java b/src/test/java/net/snowflake/client/loader/FlatfileReadMultithreadIT.java index 86f8caf5a..dae7fc196 100644 --- a/src/test/java/net/snowflake/client/loader/FlatfileReadMultithreadIT.java +++ b/src/test/java/net/snowflake/client/loader/FlatfileReadMultithreadIT.java @@ -16,13 +16,13 @@ import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.category.TestCategoryLoader; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryLoader.class) +@Tag(TestTags.LOADER) public class FlatfileReadMultithreadIT { private final int NUM_RECORDS = 100000; @@ -30,7 +30,7 @@ public class FlatfileReadMultithreadIT { private static String TARGET_SCHEMA; private static String TARGET_DB; - @BeforeClass + @BeforeAll public static void setUpClass() throws Throwable { try (Connection testConnection = AbstractDriverIT.getConnection(); // NOTE: the stage object must be created right after the connection @@ -43,7 +43,7 @@ public static void setUpClass() throws Throwable { } } - @AfterClass + @AfterAll public static void tearDownClass() throws Throwable { try (Connection testConnection = AbstractDriverIT.getConnection(); Statement statement = testConnection.createStatement()) { diff --git a/src/test/java/net/snowflake/client/loader/LoaderBase.java b/src/test/java/net/snowflake/client/loader/LoaderBase.java index ea0c29fdf..853955862 100644 --- a/src/test/java/net/snowflake/client/loader/LoaderBase.java +++ b/src/test/java/net/snowflake/client/loader/LoaderBase.java @@ -6,8 +6,8 @@ import java.sql.Connection; import java.sql.SQLException; import net.snowflake.client.AbstractDriverIT; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; public class LoaderBase { static final String TARGET_TABLE_NAME = "LOADER_test_TABLE"; @@ -16,7 +16,7 @@ public class LoaderBase { static Connection putConnection; static String SCHEMA_NAME; - @BeforeClass + @BeforeAll public static void setUpClass() throws Throwable { testConnection = AbstractDriverIT.getConnection(); putConnection = AbstractDriverIT.getConnection(); @@ -40,7 +40,7 @@ public static void setUpClass() throws Throwable { .execute("alter session set JDBC_QUERY_RESULT_FORMAT='ARROW', QUERY_RESULT_FORMAT='ARROW'"); } - @AfterClass + @AfterAll public static void tearDownClass() throws SQLException { testConnection .createStatement() diff --git a/src/test/java/net/snowflake/client/loader/LoaderIT.java b/src/test/java/net/snowflake/client/loader/LoaderIT.java index 00fea060f..7f4e3ee97 100644 --- a/src/test/java/net/snowflake/client/loader/LoaderIT.java +++ b/src/test/java/net/snowflake/client/loader/LoaderIT.java @@ -9,8 +9,8 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.ResultSet; import java.sql.SQLException; @@ -22,13 +22,13 @@ import java.util.Date; import java.util.Random; import java.util.TimeZone; -import net.snowflake.client.category.TestCategoryLoader; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** Loader IT */ -@Category(TestCategoryLoader.class) +@Tag(TestTags.LOADER) public class LoaderIT extends LoaderBase { @Test public void testInjectBadStagedFileInsert() throws Exception { @@ -93,7 +93,7 @@ public void testExecuteBeforeAfterSQLError() throws Exception { * * @throws Exception raises an exception if any error occurs. */ - @Ignore("Performance test") + @Disabled("Performance test") @Test public void testLoaderLargeInsert() throws Exception { new TestDataConfigBuilder(testConnection, putConnection) diff --git a/src/test/java/net/snowflake/client/loader/LoaderLatestIT.java b/src/test/java/net/snowflake/client/loader/LoaderLatestIT.java index e10a606d4..72212171b 100644 --- a/src/test/java/net/snowflake/client/loader/LoaderLatestIT.java +++ b/src/test/java/net/snowflake/client/loader/LoaderLatestIT.java @@ -4,8 +4,8 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -13,9 +13,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; -import net.snowflake.client.category.TestCategoryLoader; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** * Loader API tests for the latest JDBC driver. This doesn't work for the oldest supported driver. @@ -23,7 +23,7 @@ * is not applicable. If it is applicable, move tests to LoaderIT so that both the latest and oldest * supported driver run the tests. */ -@Category(TestCategoryLoader.class) +@Tag(TestTags.LOADER) public class LoaderLatestIT extends LoaderBase { @Test public void testLoaderUpsert() throws Exception { diff --git a/src/test/java/net/snowflake/client/loader/LoaderMultipleBatchIT.java b/src/test/java/net/snowflake/client/loader/LoaderMultipleBatchIT.java index 859533686..a01dfffa5 100644 --- a/src/test/java/net/snowflake/client/loader/LoaderMultipleBatchIT.java +++ b/src/test/java/net/snowflake/client/loader/LoaderMultipleBatchIT.java @@ -5,16 +5,16 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.ResultSet; import java.sql.Statement; import java.util.List; -import net.snowflake.client.category.TestCategoryLoader; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryLoader.class) +@Tag(TestTags.LOADER) public class LoaderMultipleBatchIT extends LoaderBase { @Test public void testLoaderMultipleBatch() throws Exception { diff --git a/src/test/java/net/snowflake/client/loader/LoaderTimestampIT.java b/src/test/java/net/snowflake/client/loader/LoaderTimestampIT.java index 790249e96..9c418c421 100644 --- a/src/test/java/net/snowflake/client/loader/LoaderTimestampIT.java +++ b/src/test/java/net/snowflake/client/loader/LoaderTimestampIT.java @@ -5,7 +5,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.sql.ResultSet; import java.sql.Statement; @@ -14,11 +14,11 @@ import java.util.Arrays; import java.util.Date; import java.util.TimeZone; -import net.snowflake.client.category.TestCategoryLoader; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryLoader.class) +@Tag(TestTags.LOADER) public class LoaderTimestampIT extends LoaderBase { @Test public void testLoadTimestamp() throws Exception { diff --git a/src/test/java/net/snowflake/client/loader/OnErrorTest.java b/src/test/java/net/snowflake/client/loader/OnErrorTest.java index db31b59b5..062621051 100644 --- a/src/test/java/net/snowflake/client/loader/OnErrorTest.java +++ b/src/test/java/net/snowflake/client/loader/OnErrorTest.java @@ -6,7 +6,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class OnErrorTest { @Test diff --git a/src/test/java/net/snowflake/client/log/AbstractLoggerIT.java b/src/test/java/net/snowflake/client/log/AbstractLoggerIT.java index 006574d66..15ee56e6a 100644 --- a/src/test/java/net/snowflake/client/log/AbstractLoggerIT.java +++ b/src/test/java/net/snowflake/client/log/AbstractLoggerIT.java @@ -3,21 +3,21 @@ */ package net.snowflake.client.log; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; -import net.snowflake.client.category.TestCategoryCore; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; /** A base class for testing implementations of {@link SFLogger} */ -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public abstract class AbstractLoggerIT { public static final String fakeCreds = "credentials=(aws_key_id='abc123' aws_secret_key='rtyuiop')"; - @Before + @BeforeEach void setUp() { setLogLevel(LogLevel.TRACE); } @@ -36,7 +36,7 @@ public void TestLambdaIsNotEvaluatedIfMsgIsNotLogged() { "Value: {}", (ArgSupplier) () -> { - Assert.fail("Lambda expression evaluated even though message " + "is not logged"); + fail("Lambda expression evaluated even though message " + "is not logged"); return 0; }); } @@ -103,19 +103,19 @@ private void logAndVerifyAtEachLogLevel(String expectedLogMsg, String msg, Objec String loggedMsg = getLoggedMessage(); assertEquals( + expectedLogMsg, + loggedMsg, String.format( "Message logged did not match expected value. " + "expected=%s actual=%s", - expectedLogMsg, loggedMsg), - expectedLogMsg, - loggedMsg); + expectedLogMsg, loggedMsg)); LogLevel loggedMsgLevel = getLoggedMessageLevel(); assertEquals( + level, + loggedMsgLevel, String.format( "Message was not logged at expected log level. " + "expected=%s actual=%s", - level.toString(), loggedMsgLevel.toString()), - level, - loggedMsgLevel); + level.toString(), loggedMsgLevel.toString())); } } diff --git a/src/test/java/net/snowflake/client/log/JDK14JCLWrapperLatestIT.java b/src/test/java/net/snowflake/client/log/JDK14JCLWrapperLatestIT.java index 033a15457..b8c2b63e5 100644 --- a/src/test/java/net/snowflake/client/log/JDK14JCLWrapperLatestIT.java +++ b/src/test/java/net/snowflake/client/log/JDK14JCLWrapperLatestIT.java @@ -3,21 +3,21 @@ */ package net.snowflake.client.log; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; -import net.snowflake.client.category.TestCategoryCore; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class JDK14JCLWrapperLatestIT { JDK14JCLWrapper wrapper = new JDK14JCLWrapper(JDK14JCLWrapperLatestIT.class.getName()); JDK14Logger logger = (JDK14Logger) wrapper.getLogger(); @@ -66,7 +66,7 @@ private enum LogLevel { private TestJDK14LogHandler handler = new TestJDK14LogHandler(new SFFormatter()); - @Before + @BeforeEach public void setUp() { logLevelToRestore = logger.getLevel(); // Set debug level to lowest so that all possible messages can be sent. @@ -75,7 +75,7 @@ public void setUp() { logger.setUseParentHandlers(false); } - @After + @AfterEach public void tearDown() { logger.setUseParentHandlers(true); logger.setLevel(logLevelToRestore); diff --git a/src/test/java/net/snowflake/client/log/JDK14LoggerLatestIT.java b/src/test/java/net/snowflake/client/log/JDK14LoggerLatestIT.java index 7bcfaa216..54d21f4e6 100644 --- a/src/test/java/net/snowflake/client/log/JDK14LoggerLatestIT.java +++ b/src/test/java/net/snowflake/client/log/JDK14LoggerLatestIT.java @@ -8,15 +8,15 @@ import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; -import net.snowflake.client.category.TestCategoryCore; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; /** A class for testing {@link JDK14Logger} */ -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class JDK14LoggerLatestIT extends AbstractLoggerIT { /** {@link JDK14Logger} instance that will be tested in this class */ private static final JDK14Logger LOGGER = new JDK14Logger(JDK14LoggerLatestIT.class.getName()); @@ -53,7 +53,7 @@ public class JDK14LoggerLatestIT extends AbstractLoggerIT { /** Level at which last message was logged using JDK14Logger. */ private Level lastLogMessageLevel = null; - @BeforeClass + @BeforeAll public static void oneTimeSetUp() { logLevelToRestore = internalLogger.getLevel(); useParentHandlersToRestore = internalLogger.getUseParentHandlers(); @@ -61,19 +61,19 @@ public static void oneTimeSetUp() { internalLogger.setUseParentHandlers(false); } - @AfterClass + @AfterAll public static void oneTimeTearDown() { internalLogger.setLevel(logLevelToRestore); internalLogger.setUseParentHandlers(useParentHandlersToRestore); } - @Before + @BeforeEach public void setUp() { super.setUp(); internalLogger.addHandler(this.handler); } - @After + @AfterEach public void tearDown() { internalLogger.removeHandler(this.handler); } diff --git a/src/test/java/net/snowflake/client/log/JDK14LoggerTest.java b/src/test/java/net/snowflake/client/log/JDK14LoggerTest.java index e4aadfb14..101c5f9c8 100644 --- a/src/test/java/net/snowflake/client/log/JDK14LoggerTest.java +++ b/src/test/java/net/snowflake/client/log/JDK14LoggerTest.java @@ -4,17 +4,19 @@ package net.snowflake.client.log; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.nio.file.Paths; import java.util.logging.Level; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; public class JDK14LoggerTest { @Test + @Disabled public void testLegacyLoggerInit() throws IOException { System.setProperty("snowflake.jdbc.log.size", "100000"); System.setProperty("snowflake.jdbc.log.count", "3"); diff --git a/src/test/java/net/snowflake/client/log/JDK14LoggerWithClientLatestIT.java b/src/test/java/net/snowflake/client/log/JDK14LoggerWithClientLatestIT.java index 5c11cdf22..c1f9df5df 100644 --- a/src/test/java/net/snowflake/client/log/JDK14LoggerWithClientLatestIT.java +++ b/src/test/java/net/snowflake/client/log/JDK14LoggerWithClientLatestIT.java @@ -1,9 +1,9 @@ package net.snowflake.client.log; import static net.snowflake.client.jdbc.SnowflakeUtil.systemGetProperty; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; @@ -18,30 +18,43 @@ import java.util.Properties; import java.util.logging.Level; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnWin; -import net.snowflake.client.category.TestCategoryOthers; +import net.snowflake.client.annotations.DontRunOnWindows; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.jdbc.SnowflakeSQLException; import net.snowflake.client.jdbc.SnowflakeSQLLoggedException; import org.apache.commons.io.FileUtils; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.TemporaryFolder; - -@Category(TestCategoryOthers.class) +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +@Tag(TestTags.OTHERS) public class JDK14LoggerWithClientLatestIT extends AbstractDriverIT { - @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); - + @TempDir public File tmpFolder; String homePath = systemGetProperty("user.home"); + private static Level originalLevel; + + @BeforeAll + static void saveLevel() { + originalLevel = JDK14Logger.getLevel(); + } + + @AfterAll + static void restoreLevel() { + JDK14Logger.setLevel(originalLevel); + } @Test - @Ignore + @Disabled public void testJDK14LoggingWithClientConfig() throws IOException { - File configFile = tmpFolder.newFile("config.json"); + File configFile = new File(tmpFolder, "config.json"); + configFile.createNewFile(); Path configFilePath = configFile.toPath(); - File logFolder = tmpFolder.newFolder("logs"); + File logFolder = new File(tmpFolder, "logs"); + logFolder.createNewFile(); Path logFolderPath = logFolder.toPath(); String configJson = "{\"common\":{\"log_level\":\"debug\",\"log_path\":\"" + logFolderPath + "\"}}"; @@ -63,31 +76,37 @@ public void testJDK14LoggingWithClientConfig() throws IOException { } } - @Test(expected = SQLException.class) - public void testJDK14LoggingWithClientConfigInvalidConfigFilePath() throws SQLException { + @Test + public void testJDK14LoggingWithClientConfigInvalidConfigFilePath() { Path configFilePath = Paths.get("invalid.json"); Properties properties = new Properties(); properties.put("client_config_file", configFilePath.toString()); - try (Connection connection = getConnection(properties)) { - connection.createStatement().executeQuery("select 1"); - } + assertThrows( + SnowflakeSQLException.class, + () -> { + try (Connection connection = getConnection(properties)) { + connection.createStatement().executeQuery("select 1"); + } + }); } @Test - @Ignore - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnWin.class) + @Disabled + @DontRunOnWindows public void testJDK14LoggingWithClientConfigPermissionError() throws IOException { - File configFile = tmpFolder.newFile("config.json"); + File configFile = new File(tmpFolder, "config.json"); + configFile.createNewFile(); Path configFilePath = configFile.toPath(); - File directory = tmpFolder.newFolder("logs"); - Path directoryPath = directory.toPath(); + File logFolder = new File(tmpFolder, "logs"); + logFolder.createNewFile(); + Path logFolderPath = logFolder.toPath(); String configJson = - "{\"common\":{\"log_level\":\"debug\",\"log_path\":\"" + directoryPath + "\"}}"; + "{\"common\":{\"log_level\":\"debug\",\"log_path\":\"" + logFolderPath + "\"}}"; HashSet perms = new HashSet<>(); perms.add(PosixFilePermission.OWNER_READ); perms.add(PosixFilePermission.GROUP_READ); perms.add(PosixFilePermission.OTHERS_READ); - Files.setPosixFilePermissions(directoryPath, perms); + Files.setPosixFilePermissions(logFolderPath, perms); Files.write(configFilePath, configJson.getBytes()); Properties properties = new Properties(); @@ -112,12 +131,13 @@ public void testJDK14LoggerWithQuotesInMessage() { } @Test - @Ignore + @Disabled public void testJDK14LoggingWithMissingLogPathClientConfig() throws Exception { - File configFile = tmpFolder.newFile("config.json"); + File configFile = new File(tmpFolder, "config.json"); + configFile.createNewFile(); Path configFilePath = configFile.toPath(); String configJson = "{\"common\":{\"log_level\":\"debug\"}}"; - Path home = tmpFolder.getRoot().toPath(); + Path home = tmpFolder.toPath(); System.setProperty("user.home", home.toString()); Path homeLogPath = Paths.get(home.toString(), "jdbc"); @@ -142,11 +162,11 @@ public void testJDK14LoggingWithMissingLogPathClientConfig() throws Exception { } @Test - @Ignore + @Disabled public void testJDK14LoggingWithMissingLogPathNoHomeDirClientConfig() throws Exception { System.clearProperty("user.home"); - File configFile = tmpFolder.newFile("config.json"); + File configFile = new File(tmpFolder, "config.json"); Path configFilePath = configFile.toPath(); String configJson = "{\"common\":{\"log_level\":\"debug\"}}"; Files.write(configFilePath, configJson.getBytes()); diff --git a/src/test/java/net/snowflake/client/log/SFFormatterTest.java b/src/test/java/net/snowflake/client/log/SFFormatterTest.java index 3255a7357..04ef08c02 100644 --- a/src/test/java/net/snowflake/client/log/SFFormatterTest.java +++ b/src/test/java/net/snowflake/client/log/SFFormatterTest.java @@ -4,7 +4,7 @@ package net.snowflake.client.log; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.text.DateFormat; import java.text.ParseException; @@ -15,8 +15,8 @@ import java.util.logging.Formatter; import java.util.logging.Level; import java.util.logging.LogRecord; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class SFFormatterTest { // Change these numbers if necessary @@ -28,7 +28,7 @@ public class SFFormatterTest { /** Log record generator */ private LRGenerator recordGenerator; - @Before + @BeforeEach public void setUp() { recordGenerator = new LRGenerator(SFFormatter.CLASS_NAME_PREFIX + "TestClass", "TestMethod"); recordGenerator.setFormatter(new SFFormatter()); @@ -56,8 +56,8 @@ public void testUTCTimeStampSimple() throws ParseException { Date date = extractDate(record); long nowInMs = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTimeInMillis(); assertTrue( - "Time difference boundary should be less than " + TIME_DIFFERENCE_BOUNDARY + "ms", - nowInMs - date.getTime() < TIME_DIFFERENCE_BOUNDARY); + nowInMs - date.getTime() < TIME_DIFFERENCE_BOUNDARY, + "Time difference boundary should be less than " + TIME_DIFFERENCE_BOUNDARY + "ms"); } finally { TimeZone.setDefault(originalTz); } diff --git a/src/test/java/net/snowflake/client/log/SFLogLevelTest.java b/src/test/java/net/snowflake/client/log/SFLogLevelTest.java index 5604fa013..e12271639 100644 --- a/src/test/java/net/snowflake/client/log/SFLogLevelTest.java +++ b/src/test/java/net/snowflake/client/log/SFLogLevelTest.java @@ -1,8 +1,8 @@ package net.snowflake.client.log; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SFLogLevelTest { diff --git a/src/test/java/net/snowflake/client/log/SFLoggerFactoryTest.java b/src/test/java/net/snowflake/client/log/SFLoggerFactoryTest.java index cd3f73898..a79e25de8 100644 --- a/src/test/java/net/snowflake/client/log/SFLoggerFactoryTest.java +++ b/src/test/java/net/snowflake/client/log/SFLoggerFactoryTest.java @@ -3,9 +3,9 @@ */ package net.snowflake.client.log; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SFLoggerFactoryTest { diff --git a/src/test/java/net/snowflake/client/log/SFToJavaLogMapperTest.java b/src/test/java/net/snowflake/client/log/SFToJavaLogMapperTest.java index 49ee89d60..16280b1aa 100644 --- a/src/test/java/net/snowflake/client/log/SFToJavaLogMapperTest.java +++ b/src/test/java/net/snowflake/client/log/SFToJavaLogMapperTest.java @@ -1,20 +1,20 @@ package net.snowflake.client.log; import static net.snowflake.client.log.SFToJavaLogMapper.toJavaUtilLoggingLevel; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.logging.Level; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SFToJavaLogMapperTest { @Test public void testToJavaUtilLoggingLevel() { - assertEquals(toJavaUtilLoggingLevel(SFLogLevel.OFF), java.util.logging.Level.OFF); - assertEquals(toJavaUtilLoggingLevel(SFLogLevel.ERROR), java.util.logging.Level.SEVERE); - assertEquals(toJavaUtilLoggingLevel(SFLogLevel.WARN), java.util.logging.Level.WARNING); - assertEquals(toJavaUtilLoggingLevel(SFLogLevel.INFO), java.util.logging.Level.INFO); + assertEquals(toJavaUtilLoggingLevel(SFLogLevel.OFF), Level.OFF); + assertEquals(toJavaUtilLoggingLevel(SFLogLevel.ERROR), Level.SEVERE); + assertEquals(toJavaUtilLoggingLevel(SFLogLevel.WARN), Level.WARNING); + assertEquals(toJavaUtilLoggingLevel(SFLogLevel.INFO), Level.INFO); assertEquals(toJavaUtilLoggingLevel(SFLogLevel.DEBUG), Level.FINE); - assertEquals(toJavaUtilLoggingLevel(SFLogLevel.TRACE), java.util.logging.Level.FINEST); + assertEquals(toJavaUtilLoggingLevel(SFLogLevel.TRACE), Level.FINEST); } } diff --git a/src/test/java/net/snowflake/client/log/SLF4JJJCLWrapperLatestIT.java b/src/test/java/net/snowflake/client/log/SLF4JJJCLWrapperLatestIT.java index 0b7d55a3c..008f356a0 100644 --- a/src/test/java/net/snowflake/client/log/SLF4JJJCLWrapperLatestIT.java +++ b/src/test/java/net/snowflake/client/log/SLF4JJJCLWrapperLatestIT.java @@ -3,22 +3,22 @@ */ package net.snowflake.client.log; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.AppenderBase; -import net.snowflake.client.category.TestCategoryCore; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class SLF4JJJCLWrapperLatestIT { /** Message last logged using SLF4JLogger. */ @@ -55,7 +55,7 @@ private enum LogLevel { Logger logger = (Logger) wrapper.getLogger(); private final Appender testAppender = new TestAppender(); - @Before + @BeforeEach public void setUp() { levelToRestore = logger.getLevel(); if (!testAppender.isStarted()) { @@ -66,7 +66,7 @@ public void setUp() { logger.addAppender(testAppender); } - @After + @AfterEach public void tearDown() { logger.setLevel(levelToRestore); logger.detachAppender(testAppender); diff --git a/src/test/java/net/snowflake/client/log/SLF4JLoggerLatestIT.java b/src/test/java/net/snowflake/client/log/SLF4JLoggerLatestIT.java index 79e9829f7..9e515b03a 100644 --- a/src/test/java/net/snowflake/client/log/SLF4JLoggerLatestIT.java +++ b/src/test/java/net/snowflake/client/log/SLF4JLoggerLatestIT.java @@ -11,16 +11,16 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import net.snowflake.client.category.TestCategoryCore; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.slf4j.LoggerFactory; /** A class for testing {@link SLF4JLogger} */ -@Category(TestCategoryCore.class) +@Tag(TestTags.CORE) public class SLF4JLoggerLatestIT extends AbstractLoggerIT { /** {@link SLF4JLogger} instance that will be tested in this class */ private static final SLF4JLogger LOGGER = new SLF4JLogger(SLF4JLoggerLatestIT.class); @@ -65,7 +65,7 @@ public class SLF4JLoggerLatestIT extends AbstractLoggerIT { /** Level at which last message was logged using SLF4JLogger. */ private Level lastLogMessageLevel = null; - @BeforeClass + @BeforeAll public static void oneTimeSetUp() { logLevelToRestore = internalLogger.getLevel(); additivityToRestore = internalLogger.isAdditive(); @@ -85,7 +85,7 @@ public static void oneTimeSetUp() { internalLogger.setAdditive(false); } - @AfterClass + @AfterAll public static void oneTimeTearDown() { // Restore original configuration internalLogger.setLevel(logLevelToRestore); @@ -96,10 +96,9 @@ public static void oneTimeTearDown() { appendersToRestore.forEach(internalLogger::addAppender); } - @Before + @BeforeEach public void setUp() { super.setUp(); - if (!testAppender.isStarted()) { testAppender.start(); } @@ -107,7 +106,7 @@ public void setUp() { internalLogger.addAppender(testAppender); } - @After + @AfterEach public void tearDown() { internalLogger.detachAppender(testAppender); } diff --git a/src/test/java/net/snowflake/client/pooling/ConnectionPoolingDataSourceIT.java b/src/test/java/net/snowflake/client/pooling/ConnectionPoolingDataSourceIT.java index eadd984cc..09ffe213a 100644 --- a/src/test/java/net/snowflake/client/pooling/ConnectionPoolingDataSourceIT.java +++ b/src/test/java/net/snowflake/client/pooling/ConnectionPoolingDataSourceIT.java @@ -8,7 +8,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; import java.sql.SQLException; @@ -20,11 +20,11 @@ import javax.sql.ConnectionEventListener; import javax.sql.PooledConnection; import net.snowflake.client.AbstractDriverIT; -import net.snowflake.client.category.TestCategoryConnection; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class ConnectionPoolingDataSourceIT extends AbstractDriverIT { @Test public void testPooledConnection() throws SQLException { diff --git a/src/test/java/net/snowflake/client/pooling/LogicalConnectionAlreadyClosedLatestIT.java b/src/test/java/net/snowflake/client/pooling/LogicalConnectionAlreadyClosedLatestIT.java index ce93928ac..268989657 100644 --- a/src/test/java/net/snowflake/client/pooling/LogicalConnectionAlreadyClosedLatestIT.java +++ b/src/test/java/net/snowflake/client/pooling/LogicalConnectionAlreadyClosedLatestIT.java @@ -7,12 +7,12 @@ import java.sql.SQLException; import java.util.Map; import javax.sql.PooledConnection; -import net.snowflake.client.category.TestCategoryConnection; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.BaseJDBCTest; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class LogicalConnectionAlreadyClosedLatestIT extends BaseJDBCTest { @Test diff --git a/src/test/java/net/snowflake/client/pooling/LogicalConnectionFeatureNotSupportedLatestIT.java b/src/test/java/net/snowflake/client/pooling/LogicalConnectionFeatureNotSupportedLatestIT.java index 39df72aa2..d3d19c8cf 100644 --- a/src/test/java/net/snowflake/client/pooling/LogicalConnectionFeatureNotSupportedLatestIT.java +++ b/src/test/java/net/snowflake/client/pooling/LogicalConnectionFeatureNotSupportedLatestIT.java @@ -11,12 +11,12 @@ import java.util.HashMap; import java.util.Map; import javax.sql.PooledConnection; -import net.snowflake.client.category.TestCategoryConnection; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.BaseJDBCTest; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class LogicalConnectionFeatureNotSupportedLatestIT extends BaseJDBCTest { @Test diff --git a/src/test/java/net/snowflake/client/pooling/LogicalConnectionLatestIT.java b/src/test/java/net/snowflake/client/pooling/LogicalConnectionLatestIT.java index d25cdb485..70afaf2bc 100644 --- a/src/test/java/net/snowflake/client/pooling/LogicalConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/pooling/LogicalConnectionLatestIT.java @@ -3,12 +3,12 @@ */ package net.snowflake.client.pooling; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -27,14 +27,14 @@ import java.util.Map; import java.util.Properties; import javax.sql.PooledConnection; -import net.snowflake.client.category.TestCategoryConnection; +import net.snowflake.client.category.TestTags; import net.snowflake.client.jdbc.BaseJDBCTest; import net.snowflake.client.jdbc.SnowflakeConnectionV1; import net.snowflake.client.jdbc.SnowflakeDriver; -import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; -@Category(TestCategoryConnection.class) +@Tag(TestTags.CONNECTION) public class LogicalConnectionLatestIT extends BaseJDBCTest { Map properties = getConnectionParameters(); diff --git a/src/test/java/net/snowflake/client/providers/BooleanProvider.java b/src/test/java/net/snowflake/client/providers/BooleanProvider.java new file mode 100644 index 000000000..24d2a09d3 --- /dev/null +++ b/src/test/java/net/snowflake/client/providers/BooleanProvider.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.providers; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; + +public class BooleanProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return Arrays.asList(Arguments.of(true), Arguments.of(false)); + } +} diff --git a/src/test/java/net/snowflake/client/providers/ProvidersUtil.java b/src/test/java/net/snowflake/client/providers/ProvidersUtil.java new file mode 100644 index 000000000..05b4f64c4 --- /dev/null +++ b/src/test/java/net/snowflake/client/providers/ProvidersUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.providers; + +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; + +public class ProvidersUtil { + private ProvidersUtil() {} + + private static List cartesianProduct( + ExtensionContext context, List a, SnowflakeArgumentsProvider b) { + List argsB = b.rawArguments(context); + List result = new ArrayList<>(); + for (Arguments args : a) { + for (Arguments args2 : argsB) { + result.add(Arguments.of(ArrayUtils.addAll(args.get(), args2.get()))); + } + } + return result; + } + + public static List cartesianProduct( + ExtensionContext context, + SnowflakeArgumentsProvider provider, + SnowflakeArgumentsProvider... providers) { + List args = provider.rawArguments(context); + for (SnowflakeArgumentsProvider argProvider : providers) { + args = cartesianProduct(context, args, argProvider); + } + return args; + } +} diff --git a/src/test/java/net/snowflake/client/providers/ResultFormatProvider.java b/src/test/java/net/snowflake/client/providers/ResultFormatProvider.java new file mode 100644 index 000000000..8f7ffbac4 --- /dev/null +++ b/src/test/java/net/snowflake/client/providers/ResultFormatProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.providers; + +import java.util.Arrays; +import java.util.List; +import net.snowflake.client.jdbc.ResultSetFormatType; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; + +public class ResultFormatProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + return Arrays.asList( + Arguments.of(ResultSetFormatType.JSON), + Arguments.of(ResultSetFormatType.ARROW_WITH_JSON_STRUCTURED_TYPES), + Arguments.of(ResultSetFormatType.NATIVE_ARROW)); + } +} diff --git a/src/test/java/net/snowflake/client/providers/ScaleProvider.java b/src/test/java/net/snowflake/client/providers/ScaleProvider.java new file mode 100644 index 000000000..e94421cb0 --- /dev/null +++ b/src/test/java/net/snowflake/client/providers/ScaleProvider.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.providers; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; + +public class ScaleProvider extends SnowflakeArgumentsProvider { + @Override + protected List rawArguments(ExtensionContext context) { + ArrayList scales = new ArrayList<>(); + for (int scale = 0; scale < 10; scale++) { + scales.add(Arguments.of(scale)); + } + return scales; + } +} diff --git a/src/test/java/net/snowflake/client/providers/SimpleResultFormatProvider.java b/src/test/java/net/snowflake/client/providers/SimpleResultFormatProvider.java new file mode 100644 index 000000000..1b973f966 --- /dev/null +++ b/src/test/java/net/snowflake/client/providers/SimpleResultFormatProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.providers; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; + +public class SimpleResultFormatProvider extends SnowflakeArgumentsProvider { + private static List arguments = + Arrays.asList(Arguments.of("JSON"), Arguments.of("ARROW")); + + public static void setSupportedFormats(List supportedFormats) { + arguments = supportedFormats; + } + + public static void resetSupportedFormats() { + setSupportedFormats(Arrays.asList(Arguments.of("JSON"), Arguments.of("ARROW"))); + } + + @Override + protected List rawArguments(ExtensionContext context) { + return arguments; + } +} diff --git a/src/test/java/net/snowflake/client/providers/SnowflakeArgumentsProvider.java b/src/test/java/net/snowflake/client/providers/SnowflakeArgumentsProvider.java new file mode 100644 index 000000000..28d9d48d7 --- /dev/null +++ b/src/test/java/net/snowflake/client/providers/SnowflakeArgumentsProvider.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.providers; + +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +public abstract class SnowflakeArgumentsProvider implements ArgumentsProvider { + protected abstract List rawArguments(ExtensionContext context); + + @Override + public Stream provideArguments(ExtensionContext context) { + return rawArguments(context).stream(); + } +} diff --git a/src/test/java/net/snowflake/client/providers/TimezoneProvider.java b/src/test/java/net/snowflake/client/providers/TimezoneProvider.java new file mode 100644 index 000000000..163b982c7 --- /dev/null +++ b/src/test/java/net/snowflake/client/providers/TimezoneProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.providers; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; + +public class TimezoneProvider extends SnowflakeArgumentsProvider { + private int length; + + private static List timeZones = + Arrays.asList( + Arguments.of("UTC"), + Arguments.of("America/Los_Angeles"), + Arguments.of("America/New_York"), + Arguments.of("Pacific/Honolulu"), + Arguments.of("Asia/Singapore"), + Arguments.of("CET"), + Arguments.of("GMT+0200")); + + public TimezoneProvider(int length) { + this.length = length; + } + + public TimezoneProvider() { + this.length = timeZones.size(); + } + + @Override + protected List rawArguments(ExtensionContext context) { + return timeZones.subList(0, length); + } +} diff --git a/src/test/java/net/snowflake/client/suites/ArrowTestSuite.java b/src/test/java/net/snowflake/client/suites/ArrowTestSuite.java new file mode 100644 index 000000000..b0bfa532a --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/ArrowTestSuite.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@BaseTestSuite +@IncludeTags(TestTags.ARROW) +public class ArrowTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/BaseTestSuite.java b/src/test/java/net/snowflake/client/suites/BaseTestSuite.java new file mode 100644 index 000000000..42b3d9a53 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/BaseTestSuite.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.platform.suite.api.ExcludePackages; +import org.junit.platform.suite.api.IncludeClassNamePatterns; +import org.junit.platform.suite.api.SelectPackages; +import org.junit.platform.suite.api.Suite; +import org.junit.platform.suite.api.SuiteDisplayName; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Suite +@SuiteDisplayName("Testowanie") +@SelectPackages("net.snowflake.client") +@ExcludePackages("net.snowflake.client.suites") +@IncludeClassNamePatterns(".+") +public @interface BaseTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/ConnectionOldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/ConnectionOldDriverTestSuite.java new file mode 100644 index 000000000..6dc07481d --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/ConnectionOldDriverTestSuite.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@IncludeTags(TestTags.CONNECTION) +public class ConnectionOldDriverTestSuite extends OldDriverTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/ConnectionTestSuite.java b/src/test/java/net/snowflake/client/suites/ConnectionTestSuite.java new file mode 100644 index 000000000..6ebbd1237 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/ConnectionTestSuite.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@BaseTestSuite +@IncludeTags(TestTags.CONNECTION) +public class ConnectionTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/CoreOldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/CoreOldDriverTestSuite.java new file mode 100644 index 000000000..be0763f55 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/CoreOldDriverTestSuite.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@IncludeTags(TestTags.CORE) +public class CoreOldDriverTestSuite extends OldDriverTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/CoreTestSuite.java b/src/test/java/net/snowflake/client/suites/CoreTestSuite.java new file mode 100644 index 000000000..3e7a15db1 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/CoreTestSuite.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@BaseTestSuite +@IncludeTags(TestTags.CORE) +public class CoreTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/DiagnosticOldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/DiagnosticOldDriverTestSuite.java new file mode 100644 index 000000000..cdc925ecb --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/DiagnosticOldDriverTestSuite.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@IncludeTags(TestTags.DIAGNOSTIC) +public class DiagnosticOldDriverTestSuite extends OldDriverTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/DiagnosticTestSuite.java b/src/test/java/net/snowflake/client/suites/DiagnosticTestSuite.java new file mode 100644 index 000000000..18a53668c --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/DiagnosticTestSuite.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@BaseTestSuite +@IncludeTags(TestTags.DIAGNOSTIC) +public class DiagnosticTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/LoaderOldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/LoaderOldDriverTestSuite.java new file mode 100644 index 000000000..897613378 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/LoaderOldDriverTestSuite.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@IncludeTags(TestTags.LOADER) +public class LoaderOldDriverTestSuite extends OldDriverTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/LoaderTestSuite.java b/src/test/java/net/snowflake/client/suites/LoaderTestSuite.java new file mode 100644 index 000000000..7d4952e57 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/LoaderTestSuite.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@BaseTestSuite +@IncludeTags(TestTags.LOADER) +public class LoaderTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/OldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/OldDriverTestSuite.java new file mode 100644 index 000000000..363ad3d2a --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/OldDriverTestSuite.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import java.util.Arrays; +import net.snowflake.client.providers.SimpleResultFormatProvider; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.platform.suite.api.AfterSuite; +import org.junit.platform.suite.api.BeforeSuite; + +@BaseTestSuite +public abstract class OldDriverTestSuite { + @BeforeSuite + public static void beforeAll() { + SimpleResultFormatProvider.setSupportedFormats(Arrays.asList(Arguments.of("JSON"))); + } + + @AfterSuite + public static void afterAll() { + SimpleResultFormatProvider.resetSupportedFormats(); + } +} diff --git a/src/test/java/net/snowflake/client/suites/OthersOldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/OthersOldDriverTestSuite.java new file mode 100644 index 000000000..3562d9c0e --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/OthersOldDriverTestSuite.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@IncludeTags(TestTags.OTHERS) +public class OthersOldDriverTestSuite extends OldDriverTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/OthersTestSuite.java b/src/test/java/net/snowflake/client/suites/OthersTestSuite.java new file mode 100644 index 000000000..02f9f3630 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/OthersTestSuite.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@BaseTestSuite +@IncludeTags(TestTags.OTHERS) +public class OthersTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/ResultSetOldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/ResultSetOldDriverTestSuite.java new file mode 100644 index 000000000..a57873e80 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/ResultSetOldDriverTestSuite.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@IncludeTags(TestTags.RESULT_SET) +public class ResultSetOldDriverTestSuite extends OldDriverTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/ResultSetTestSuite.java b/src/test/java/net/snowflake/client/suites/ResultSetTestSuite.java new file mode 100644 index 000000000..0032593c2 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/ResultSetTestSuite.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@BaseTestSuite +@IncludeTags(TestTags.RESULT_SET) +public class ResultSetTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/StatementOldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/StatementOldDriverTestSuite.java new file mode 100644 index 000000000..62ece4cec --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/StatementOldDriverTestSuite.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@IncludeTags(TestTags.STATEMENT) +public class StatementOldDriverTestSuite extends OldDriverTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/StatementTestSuite.java b/src/test/java/net/snowflake/client/suites/StatementTestSuite.java new file mode 100644 index 000000000..19b96cf34 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/StatementTestSuite.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.IncludeTags; + +@BaseTestSuite +@IncludeTags(TestTags.STATEMENT) +public class StatementTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/UnitOldDriverTestSuite.java b/src/test/java/net/snowflake/client/suites/UnitOldDriverTestSuite.java new file mode 100644 index 000000000..8c9a9f470 --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/UnitOldDriverTestSuite.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.platform.suite.api.ExcludeTags; + +@ExcludeTags({ + TestTags.CORE, + TestTags.ARROW, + TestTags.DIAGNOSTIC, + TestTags.CONNECTION, + TestTags.LOADER, + TestTags.OTHERS, + TestTags.RESULT_SET, + TestTags.STATEMENT +}) +public class UnitOldDriverTestSuite extends OldDriverTestSuite {} diff --git a/src/test/java/net/snowflake/client/suites/UnitTestSuite.java b/src/test/java/net/snowflake/client/suites/UnitTestSuite.java new file mode 100644 index 000000000..5bd5904fe --- /dev/null +++ b/src/test/java/net/snowflake/client/suites/UnitTestSuite.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Snowflake Computing Inc. All rights reserved. + */ +package net.snowflake.client.suites; + +import net.snowflake.client.category.TestTags; +import org.junit.jupiter.api.DisplayName; +import org.junit.platform.suite.api.ExcludeTags; + +@BaseTestSuite +@DisplayName("Unit tests") +@ExcludeTags({ + TestTags.CORE, + TestTags.ARROW, + TestTags.DIAGNOSTIC, + TestTags.CONNECTION, + TestTags.LOADER, + TestTags.OTHERS, + TestTags.RESULT_SET, + TestTags.STATEMENT +}) +public class UnitTestSuite {} diff --git a/src/test/java/net/snowflake/client/util/SecretDetectorTest.java b/src/test/java/net/snowflake/client/util/SecretDetectorTest.java index aa3339309..1b936b929 100644 --- a/src/test/java/net/snowflake/client/util/SecretDetectorTest.java +++ b/src/test/java/net/snowflake/client/util/SecretDetectorTest.java @@ -1,7 +1,7 @@ package net.snowflake.client.util; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -12,7 +12,7 @@ import net.minidev.json.JSONObject; import net.snowflake.client.core.ObjectMapperFactory; import org.apache.commons.lang3.RandomStringUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class SecretDetectorTest { @Test diff --git a/src/test/java/net/snowflake/client/util/StopwatchTest.java b/src/test/java/net/snowflake/client/util/StopwatchTest.java index ed5f5d743..066b450fa 100644 --- a/src/test/java/net/snowflake/client/util/StopwatchTest.java +++ b/src/test/java/net/snowflake/client/util/StopwatchTest.java @@ -7,18 +7,18 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.concurrent.TimeUnit; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class StopwatchTest { Stopwatch stopwatch = new Stopwatch(); - @Before + @BeforeEach public void before() { stopwatch = new Stopwatch(); }