From 4cd474205710775a9063084701d4756256aa09a6 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Tue, 23 Jul 2024 14:47:33 +0200 Subject: [PATCH 1/7] SNOW-XXXXXX - structured types bindings test all types --- .../client/core/FieldSchemaCreator.java | 7 +- .../client/core/SFBaseResultSet.java | 198 ++++---- .../net/snowflake/client/core/SfSqlArray.java | 16 +- .../client/core/arrow/MapConverter.java | 11 +- .../client/jdbc/SnowflakeBaseResultSet.java | 8 + .../jdbc/SnowflakePreparedStatementV1.java | 66 ++- .../snowflake/client/jdbc/SnowflakeUtil.java | 130 +++++- ...nsertingArraysStructuredTypesLatestIT.java | 395 ++++++++++++++++ ...dInsertingMapsStructuredTypesLatestIT.java | 434 ++++++++++++++++++ ...ngAndInsertingStructuredTypesLatestIT.java | 120 +---- 10 files changed, 1140 insertions(+), 245 deletions(-) create mode 100644 src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java create mode 100644 src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java diff --git a/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java b/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java index b61dbd1f8..edbad9643 100644 --- a/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java +++ b/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java @@ -85,9 +85,14 @@ public static BindingParameterMetadata buildBindingSchemaForType(int baseType, b case Types.DATE: return FieldSchemaCreator.buildSchemaTypeAndNameOnly(name, "date", Optional.empty()); case Types.TIMESTAMP: + return FieldSchemaCreator.buildSchemaWithScaleAndPrecision( + name, "timestamp", 9, 0, Optional.empty()); case Types.TIME: return FieldSchemaCreator.buildSchemaWithScaleAndPrecision( - name, "timestamp", 9, 0, Optional.empty()); + name, "time", 9, 0, Optional.empty()); + case Types.BINARY: + return FieldSchemaCreator.buildSchemaForBytesType( + name, Optional.empty()); default: logger.error("Could not create schema for type : " + baseType); throw new SQLException("Could not create schema for type : " + baseType); diff --git a/src/main/java/net/snowflake/client/core/SFBaseResultSet.java b/src/main/java/net/snowflake/client/core/SFBaseResultSet.java index 71e56a515..add713adb 100644 --- a/src/main/java/net/snowflake/client/core/SFBaseResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFBaseResultSet.java @@ -288,99 +288,111 @@ protected SfSqlArray getJsonArray(String obj, int columnIndex) throws SFExceptio int columnType = ColumnTypeHelper.getColumnType(columnSubType, session); int scale = fieldMetadata.getScale(); - ArrayNode arrayNode = (ArrayNode) OBJECT_MAPPER.readTree(obj); - Iterator nodeElements = arrayNode.elements(); - - switch (columnType) { - case Types.INTEGER: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().integerConverter(columnType)) - .toArray(Integer[]::new)); - case Types.SMALLINT: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().smallIntConverter(columnType)) - .toArray(Short[]::new)); - case Types.TINYINT: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().tinyIntConverter(columnType)) - .toArray(Byte[]::new)); - case Types.BIGINT: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().bigIntConverter(columnType)) - .toArray(Long[]::new)); - case Types.DECIMAL: - case Types.NUMERIC: - return new SfSqlArray( - columnSubType, - convertToFixedArray( - getStream(nodeElements, getConverters().bigDecimalConverter(columnType)))); - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGNVARCHAR: - return new SfSqlArray( - columnSubType, - getStream( - nodeElements, - getConverters().varcharConverter(columnType, columnSubType, scale)) - .toArray(String[]::new)); - case Types.BINARY: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().bytesConverter(columnType, scale)) - .toArray(Byte[][]::new)); - case Types.FLOAT: - case Types.REAL: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().floatConverter(columnType)) - .toArray(Float[]::new)); - case Types.DOUBLE: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().doubleConverter(columnType)) - .toArray(Double[]::new)); - case Types.DATE: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().dateStringConverter(session)) - .toArray(Date[]::new)); - case Types.TIME: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().timeFromStringConverter(session)) - .toArray(Time[]::new)); - case Types.TIMESTAMP: - return new SfSqlArray( - columnSubType, - getStream( - nodeElements, - getConverters() - .timestampFromStringConverter( - columnSubType, columnType, scale, session, null, sessionTimeZone)) - .toArray(Timestamp[]::new)); - case Types.BOOLEAN: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().booleanConverter(columnType)) - .toArray(Boolean[]::new)); - case Types.STRUCT: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().structConverter(OBJECT_MAPPER)) - .toArray(Map[]::new)); - case Types.ARRAY: - return new SfSqlArray( - columnSubType, - getStream(nodeElements, getConverters().arrayConverter(OBJECT_MAPPER)) - .toArray(Map[][]::new)); - default: - throw new SFException( - ErrorCode.FEATURE_UNSUPPORTED, - "Can't construct array for data type: " + columnSubType); + JsonNode data = OBJECT_MAPPER.readTree(obj); + + if (data.isNull()) { + return null; + } + + if (data.isArray()) { + ArrayNode arrayNode = (ArrayNode) OBJECT_MAPPER.readTree(obj); + Iterator nodeElements = arrayNode.elements(); + + switch (columnType) { + case Types.INTEGER: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().integerConverter(columnType)) + .toArray(Integer[]::new)); + case Types.SMALLINT: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().smallIntConverter(columnType)) + .toArray(Short[]::new)); + case Types.TINYINT: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().tinyIntConverter(columnType)) + .toArray(Byte[]::new)); + case Types.BIGINT: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().bigIntConverter(columnType)) + .toArray(Long[]::new)); + case Types.DECIMAL: + case Types.NUMERIC: + return new SfSqlArray( + columnSubType, + convertToFixedArray( + getStream(nodeElements, getConverters().bigDecimalConverter(columnType)))); + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGNVARCHAR: + return new SfSqlArray( + columnSubType, + getStream( + nodeElements, + getConverters().varcharConverter(columnType, columnSubType, scale)) + .toArray(String[]::new)); + case Types.BINARY: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().bytesConverter(columnType, scale)) + .toArray(Byte[][]::new)); + case Types.FLOAT: + case Types.REAL: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().floatConverter(columnType)) + .toArray(Float[]::new)); + case Types.DOUBLE: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().doubleConverter(columnType)) + .toArray(Double[]::new)); + case Types.DATE: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().dateStringConverter(session)) + .toArray(Date[]::new)); + case Types.TIME: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().timeFromStringConverter(session)) + .toArray(Time[]::new)); + case Types.TIMESTAMP: + return new SfSqlArray( + columnSubType, + getStream( + nodeElements, + getConverters() + .timestampFromStringConverter( + columnSubType, columnType, scale, session, null, sessionTimeZone)) + .toArray(Timestamp[]::new)); + case Types.BOOLEAN: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().booleanConverter(columnType)) + .toArray(Boolean[]::new)); + case Types.STRUCT: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().structConverter(OBJECT_MAPPER)) + .toArray(Map[]::new)); + case Types.ARRAY: + return new SfSqlArray( + columnSubType, + getStream(nodeElements, getConverters().arrayConverter(OBJECT_MAPPER)) + .toArray(Map[][]::new)); + default: + throw new SFException( + ErrorCode.FEATURE_UNSUPPORTED, + "Can't construct array for data type: " + columnSubType); + } + } else { + throw new SFException( + ErrorCode.INVALID_VALUE_CONVERT, + "Can't construct array from delivered data"); } } catch (JsonProcessingException e) { throw new SFException(e, ErrorCode.INVALID_STRUCT_DATA); diff --git a/src/main/java/net/snowflake/client/core/SfSqlArray.java b/src/main/java/net/snowflake/client/core/SfSqlArray.java index 70682b4f4..e1ba10e8b 100644 --- a/src/main/java/net/snowflake/client/core/SfSqlArray.java +++ b/src/main/java/net/snowflake/client/core/SfSqlArray.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.Arrays; +import java.util.List; import java.util.Map; import net.snowflake.client.jdbc.BindingParameterMetadata; import net.snowflake.client.jdbc.SnowflakeUtil; @@ -81,9 +82,13 @@ public ResultSet getResultSet(long index, int count, Map> map) @Override public void free() throws SQLException {} - public String getJsonString() throws SQLException { + public Object getElements() { + return elements; + } + + public String getArrayJsonString(int type) throws SQLException { try { - return SnowflakeUtil.mapJson(elements); + return SnowflakeUtil.mapArrayElements(elements, type, null); } catch (JsonProcessingException e) { throw new SQLException("There is exception during array to json string.", e); } @@ -95,4 +100,11 @@ public BindingParameterMetadata getSchema() throws SQLException { .withFields(Arrays.asList(buildBindingSchemaForType(getBaseType(), false))) .build(); } + + public BindingParameterMetadata getSchema(List fields) throws SQLException { + return BindingParameterMetadata.BindingParameterMetadataBuilder.bindingParameterMetadata() + .withType("array") + .withFields(fields) + .build(); + } } 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..2e26363cf 100644 --- a/src/main/java/net/snowflake/client/core/arrow/MapConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/MapConverter.java @@ -1,7 +1,8 @@ package net.snowflake.client.core.arrow; +import java.util.HashMap; import java.util.List; -import java.util.stream.Collectors; +import java.util.Map; import net.snowflake.client.core.DataConversionContext; import net.snowflake.client.core.SFException; import net.snowflake.client.jdbc.SnowflakeType; @@ -21,9 +22,11 @@ public MapConverter(MapVector valueVector, int columnIndex, DataConversionContex public Object toObject(int index) throws SFException { List> entriesList = (List>) vector.getObject(index); - return entriesList.stream() - .collect( - Collectors.toMap(entry -> entry.get("key").toString(), entry -> entry.get("value"))); + Map converted = new HashMap<>(); + for (Map map : entriesList) { + converted.put(map.get("key").toString(), map.get("value")); + } + return converted; } @Override diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java index 550acbe95..1d6eef98b 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java @@ -1729,6 +1729,14 @@ public Map getMap(int columnIndex, Class type) throws SQLExcep sfBaseResultSet.convertToTimestamp( entry.getValue(), columnType, columnSubType, tz, scale))); + } else if (byte[].class.isAssignableFrom(type)) { + resultMap.put( + entry.getKey(), + mapSFExceptionToSQLException( + () -> + (T) + sfBaseResultSet.getConverters().getBytesConverter().getBytes( entry.getValue(), columnType, columnSubType, scale))); + } else { logger.debug( "Unsupported type passed to getObject(int columnIndex,Class type): " diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java index 000d4634d..77639ca55 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java @@ -35,6 +35,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TimeZone; import net.snowflake.client.core.ExecTimeTelemetryData; @@ -388,20 +389,13 @@ public void setTime(int parameterIndex, Time x) throws SQLException { @Override public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { logger.trace("setTimestamp(parameterIndex: {}, Timestamp x)", parameterIndex); - setTimestampWithType(parameterIndex, x, Types.TIMESTAMP); } private void setTimestampWithType(int parameterIndex, Timestamp x, int snowflakeType) throws SQLException { // convert the timestamp from being in local time zone to be in UTC timezone - String value = - x == null - ? null - : String.valueOf( - BigDecimal.valueOf((x.getTime() - ResultUtil.msDiffJulianToGregorian(x)) / 1000) - .scaleByPowerOfTen(9) - .add(BigDecimal.valueOf(x.getNanos()))); + String value = prepareStringOfTimestamp(x); String bindingTypeName; switch (snowflakeType) { case SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_LTZ: @@ -419,6 +413,17 @@ private void setTimestampWithType(int parameterIndex, Timestamp x, int snowflake parameterBindings.put(String.valueOf(parameterIndex), binding); } + public static String prepareStringOfTimestamp(Timestamp x) { + String value = + x == null + ? null + : String.valueOf( + BigDecimal.valueOf((x.getTime() - ResultUtil.msDiffJulianToGregorian(x)) / 1000) + .scaleByPowerOfTen(9) + .add(BigDecimal.valueOf(x.getNanos()))); + return value; + } + @Override public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { throw new SnowflakeLoggedFeatureNotSupportedException(connection.getSFBaseSession()); @@ -624,22 +629,33 @@ public void setClob(int parameterIndex, Clob x) throws SQLException { public void setArray(int parameterIndex, Array array) throws SQLException { if (array instanceof SfSqlArray) { SfSqlArray sfArray = (SfSqlArray) array; - ParameterBindingDTO binding = - new ParameterBindingDTO( - "json", - SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), - sfArray.getJsonString(), - sfArray.getSchema()); - parameterBindings.put(String.valueOf(parameterIndex), binding); - } else { - SfSqlArray sfArray = new SfSqlArray(Types.INTEGER, array); - ParameterBindingDTO binding = - new ParameterBindingDTO( - "json", - SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), - sfArray.getJsonString(), - sfArray.getSchema()); - parameterBindings.put(String.valueOf(parameterIndex), binding); + if (sfArray.getElements() != null && SQLData.class.isAssignableFrom(sfArray.getElements().getClass().getComponentType())) { + SQLData[] objects = (SQLData[]) sfArray.getElements(); + SQLData sqlData = Optional.ofNullable(objects[0]).orElse(null); + JsonSqlOutput stream = new JsonSqlOutput(sqlData, connection.getSFBaseSession()); + sqlData.writeSQL(stream); + BindingParameterMetadata valueTypeSchema = stream.getSchema(); + + ParameterBindingDTO binding; + try { + binding = new ParameterBindingDTO( + "json", + SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), + SnowflakeUtil.mapArrayElements(objects, Types.STRUCT, connection), + sfArray.getSchema(Arrays.asList(valueTypeSchema))); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + parameterBindings.put(String.valueOf(parameterIndex), binding); + } else { + ParameterBindingDTO binding = + new ParameterBindingDTO( + "json", + SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), + sfArray.getArrayJsonString(sfArray.getBaseType()), + sfArray.getSchema()); + parameterBindings.put(String.valueOf(parameterIndex), binding); + } } } @@ -669,7 +685,7 @@ public void setMap(int parameterIndex, Map map, int type) throws new ParameterBindingDTO( "json", SnowflakeUtil.javaTypeToSFTypeString(Types.STRUCT, connection.getSFBaseSession()), - SnowflakeUtil.mapJson(map), + SnowflakeUtil.mapMapValuesJson(map, type, connection), schema); } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index efe8ffbf8..20f13f827 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -18,8 +18,11 @@ import java.io.StringWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.sql.Date; +import java.sql.SQLData; import java.sql.SQLException; import java.sql.Time; +import java.sql.Timestamp; import java.sql.Types; import java.time.Instant; import java.time.LocalDateTime; @@ -27,19 +30,24 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.Random; +import java.util.TimeZone; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import net.minidev.json.JSONObject; import net.snowflake.client.core.Constants; import net.snowflake.client.core.HttpClientSettingsKey; +import net.snowflake.client.core.JsonSqlOutput; import net.snowflake.client.core.OCSPMode; import net.snowflake.client.core.ObjectMapperFactory; +import net.snowflake.client.core.ResultUtil; import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SFException; import net.snowflake.client.core.SFSessionProperty; @@ -47,6 +55,10 @@ import net.snowflake.client.log.SFLogger; import net.snowflake.client.log.SFLoggerFactory; import net.snowflake.client.util.ThrowingCallable; +import net.snowflake.common.core.SFBinary; +import net.snowflake.common.core.SFTime; +import net.snowflake.common.core.SFTimestamp; +import net.snowflake.common.core.SnowflakeDateTimeFormat; import net.snowflake.common.core.SqlState; import net.snowflake.common.util.ClassUtil; import net.snowflake.common.util.FixedViewColumn; @@ -92,8 +104,117 @@ public class SnowflakeUtil { public static final String BYTE_STR = "byte"; public static final String BYTES_STR = "byte array"; - public static String mapJson(Object ob) throws JsonProcessingException { - return OBJECT_MAPPER.writeValueAsString(ob); + public static String mapMapValuesJson(Map map, int type, SnowflakeConnectionV1 connection ) throws JsonProcessingException, SQLException { + if (type == Types.STRUCT) { + Map jObjects = new HashMap<>(); + for (Object key : map.keySet()) { + SQLData element = (SQLData) map.get(key); + JsonSqlOutput sqlOutput= new JsonSqlOutput(element, connection.getSFBaseSession()); + element.writeSQL(sqlOutput); + jObjects.put(key, sqlOutput.getJsonObject()); + } + return OBJECT_MAPPER.writeValueAsString(jObjects); + } else if (Arrays.asList(Types.VARCHAR, + Types.CHAR, + Types.FLOAT, + Types.DOUBLE, + Types.DECIMAL, + Types.NUMERIC, + Types.INTEGER, + Types.SMALLINT, + Types.TINYINT, + Types.BIGINT, + Types.BOOLEAN, + Types.TIMESTAMP, + Types.TIME, + Types.DATE, + Types.BINARY).contains(type)) { + Map mappedValues = new HashMap<>(); + for (Object key : map.keySet()) { + if(map.get(key) == null) { + mappedValues.put(key, null); + } else { + mappedValues.put(key, formatStringForType(map.get(key), type, connection)); + } + } + return OBJECT_MAPPER.writeValueAsString(mappedValues); + } else { + throw new SQLException("Unsupported type in array: " + type); + } + } + public static String mapArrayElements(Object ob, int type, SnowflakeConnectionV1 connection) throws JsonProcessingException, SQLException { + if (ob == null) { + return OBJECT_MAPPER.writeValueAsString(ob); + } else if (type == Types.STRUCT) { + SQLData[] elements = (SQLData[]) ob; + ArrayList jObjects = new ArrayList<>(); + for (SQLData element : elements) { + JsonSqlOutput sqlOutput= new JsonSqlOutput(element, connection.getSFBaseSession()); + element.writeSQL(sqlOutput); + jObjects.add(sqlOutput.getJsonObject()); + } + return OBJECT_MAPPER.writeValueAsString(jObjects); + } else if (Arrays.asList(Types.VARCHAR, + Types.CHAR, + Types.FLOAT, + Types.DOUBLE, + Types.DECIMAL, + Types.NUMERIC, + Types.INTEGER, + Types.SMALLINT, + Types.TINYINT, + Types.BIGINT, + Types.BOOLEAN, + Types.TIMESTAMP, + Types.TIME, + Types.DATE, + Types.BINARY).contains(type)) { + Object[] array = (Object[]) ob; + ArrayList str = new ArrayList(); + for (Object element : array) { + if(element == null) { + str.add(null); + } else { + str.add(formatStringForType(element, type, connection)); + } + } + return OBJECT_MAPPER.writeValueAsString(str); + } else { + throw new SQLException("Unsupported type in array: " + type); + } + } + + private static String formatStringForType(Object value, int javaType, SnowflakeConnectionV1 connection) throws SQLException { + if ( value == null) { + return String.valueOf(value); + } else if ( javaType == Types.TIMESTAMP) { + try { + return ResultUtil.getSFTimestampAsString( + new SFTimestamp((Timestamp) value, TimeZone.getDefault()), + javaType, + 9, + getFormat(connection.getSFBaseSession(), "TIMESTAMP_NTZ_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_LTZ_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_TZ_OUTPUT_FORMAT"), + connection.getSFBaseSession()); + } catch (SFException e) { + throw new SQLException(e); + } + } else if ( javaType == Types.TIME) { + SnowflakeDateTimeFormat formatter = getFormat(connection.getSFBaseSession(), "TIME_OUTPUT_FORMAT"); + return formatter.format(SFTime.fromNanoseconds(((Time) value).getTime() * 1000 * 1000), 9); + } else if ( javaType == Types.DATE) { + Date date = (Date) value; + return String.valueOf( + date.getTime() + + TimeZone.getDefault().getOffset(date.getTime()) + - ResultUtil.msDiffJulianToGregorian(date)); + } else if ( javaType == Types.BINARY) { + byte[] bytes = (byte[]) value; + return new SFBinary(bytes).toHex(); + } else { + return String.valueOf(value); + } } public static void checkErrorAndThrowExceptionIncludingReauth(JsonNode rootNode) @@ -891,4 +1012,9 @@ public static String getJsonNodeStringValue(JsonNode node) throws SFException { } return node.isValueNode() ? node.asText() : node.toString(); } + + public static SnowflakeDateTimeFormat getFormat(SFBaseSession session, String format) { + return SnowflakeDateTimeFormat.fromSqlFormat( + (String) session.getCommonParameters().get(format)); + } } diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java new file mode 100644 index 000000000..8907605b7 --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2012-2024 Snowflake Computing Inc. All right reserved. + */ +package net.snowflake.client.jdbc; + +import net.snowflake.client.ConditionalIgnoreRule; +import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryResultSet; +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.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.sql.Array; +import java.sql.Connection; +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.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.TimeZone; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@RunWith(Parameterized.class) +@Category(TestCategoryResultSet.class) +public class BindingAndInsertingArraysStructuredTypesLatestIT 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 BindingAndInsertingArraysStructuredTypesLatestIT(ResultSetFormatType queryResultFormat) { + this.queryResultFormat = queryResultFormat; + } + + public Connection init() 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"); + stmt.execute("alter session set IGNORE_CLIENT_VESRION_IN_STRUCTURED_TYPES_RESPONSE = true"); + stmt.execute("alter session set ENABLE_STRUCTURED_TYPES_IN_BINDS = enable"); + stmt.execute("alter session set ENABLE_OBJECT_TYPED_BINDS = true"); + stmt.execute("alter session set enable_structured_types_in_fdn_tables=true"); + stmt.execute("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'"); + 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; + } + + @Before + public void setup() { + SnowflakeObjectTypeFactories.register(SimpleClass.class, SimpleClass::new); + SnowflakeObjectTypeFactories.register(AllTypesClass.class, AllTypesClass::new); + } + + @After + public void clean() { + SnowflakeObjectTypeFactories.unregister(SimpleClass.class); + SnowflakeObjectTypeFactories.unregister(AllTypesClass.class); + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayInteger() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_integers (arrayInt) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(INTEGER))"); + + Array array = connection.createArrayOf("INTEGER", new Integer[] {1, 2, 3}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { + resultSet.next(); + + Long[] resultArray = (Long[]) resultSet.getArray(1).getArray(); + assertEquals(Long.valueOf(1), resultArray[0]); + assertEquals(Long.valueOf(2), resultArray[1]); + assertEquals(Long.valueOf(3), resultArray[2]); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayString() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(VARCHAR))"); + + Array array = connection.createArrayOf("VARCHAR", new String[] {"a", "b", "c"}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { + resultSet.next(); + + String[] resultArray = (String[]) resultSet.getArray(1).getArray(); + assertEquals("a", resultArray[0]); + assertEquals("b", resultArray[1]); + assertEquals("c", resultArray[2]); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayOfTimestamp() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_integers (arrayInt) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(TIMESTAMP_LTZ))"); + + Array array = connection.createArrayOf("TIMESTAMP", + new Timestamp[] {Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { + resultSet.next(); + + Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); + assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0]); + assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[1]); + } + } + } + + @Ignore + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayOfTime() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_time (arrayType) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_time(arrayType ARRAY(TIME))"); + + Array array = connection.createArrayOf("TIME", + new Time[] {Time.valueOf("12:34:56"), Time.valueOf("12:34:57")}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_time"); ) { + resultSet.next(); + + Time[] resultArray = (Time[]) resultSet.getArray(1).getArray(); + assertEquals(Time.valueOf("12:34:56"), resultArray[0]); + assertEquals(Time.valueOf("12:34:57"), resultArray[1]); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayOfDate() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_date (arrayType) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_date(arrayType ARRAY(DATE))"); + + Array array = connection.createArrayOf("DATE", + new Date[] {Date.valueOf("2023-12-24"), Date.valueOf("2023-12-24")}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_date"); ) { + resultSet.next(); + + Date[] resultArray = (Date[]) resultSet.getArray(1).getArray(); + assertEquals(Date.valueOf("2023-12-24").toString(), resultArray[0].toString()); + assertEquals(Date.valueOf("2023-12-24").toString(), resultArray[1].toString()); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteNullArray() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(VARCHAR))"); + + Array nullArray = connection.createArrayOf("VARCHAR", null); + stmt.setArray(1, nullArray); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { + resultSet.next(); + + Object resultArray = resultSet.getArray(1); + assertNull(resultArray); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayOfSqlData() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(OBJECT(string VARCHAR, intValue INTEGER)) )"); + + Array array = connection.createArrayOf("STRUCT", + new SimpleClass[] {new SimpleClass("string1", 1), new SimpleClass("string12", 2)}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { + resultSet.next(); + + SimpleClass[] resultArray = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, SimpleClass.class); + assertEquals(Integer.valueOf(1), resultArray[0].getIntValue()); + assertEquals("string1", resultArray[0].getString()); + assertEquals(Integer.valueOf(2), resultArray[1].getIntValue()); + assertEquals("string12", resultArray[1].getString()); + } + } + } + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayOfAllTypesObject() throws SQLException { + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(OBJECT(string VARCHAR, " + + " b TINYINT, " + + " s SMALLINT, " + + " i INTEGER, " + + " l BIGINT, " + + " f FLOAT, " + + " d DOUBLE, " + + " bd NUMBER(38,2), " + + " bool BOOLEAN, " + + " timestampLtz TIMESTAMP_LTZ, " + + " timestampNtz TIMESTAMP_NTZ, " + + " timestampTz TIMESTAMP_TZ, " + + " date DATE," + + " time TIME, " + + " binary BINARY, " + + " simpleClass OBJECT(string VARCHAR, intValue INTEGER)" + + " ) ) )"); + + Array array = connection.createArrayOf("STRUCT", + new AllTypesClass[] { + new AllTypesClass( + "string", + "1".getBytes(StandardCharsets.UTF_8)[0], + Short.valueOf("2"), + Integer.valueOf(3), + Long.valueOf(4), + 1.1f, + 2.24, + new BigDecimal("999999999999999999999999999999999999.55"), + Boolean.TRUE, + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), + Date.valueOf("2023-12-24"), + Time.valueOf("12:34:56"), + new byte[] {'a', 'b', 'c'}, + new SimpleClass("testString", 2))}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { + resultSet.next(); + + AllTypesClass[] resultArray = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, AllTypesClass.class); + System.out.println(resultArray); + assertEquals("string", resultArray[0].getString()); + assertEquals(49, (long) resultArray[0].getB()); + assertEquals(2, (long) resultArray[0].getS()); + assertEquals(3, (long) resultArray[0].getI()); + assertEquals(4, (long) resultArray[0].getL()); + assertEquals(1.1, (double) resultArray[0].getF(), 0.01); + assertEquals(2.24, (double) resultArray[0].getD(), 0.01); + assertEquals(new BigDecimal("999999999999999999999999999999999999.55"), resultArray[0].getBd()); + assertEquals(Boolean.TRUE, resultArray[0].getBool()); + assertEquals( + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0].getTimestampLtz()); + assertEquals( + Timestamp.valueOf(LocalDateTime.of(2021, 12, 23, 9, 44, 44)), resultArray[0].getTimestampNtz()); + assertEquals( + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), + resultArray[0].getTimestampTz()); + // TODO uncomment after merge SNOW-928973: Date field is returning one day less when getting + // through getString method + // assertEquals(Date.valueOf(LocalDate.of(2023, 12, 24)), resultArray[0].getDate()); + assertEquals(Time.valueOf(LocalTime.of(12, 34, 56)), resultArray[0].getTime()); + assertArrayEquals(new byte[] {'a', 'b', 'c'}, resultArray[0].getBinary()); + assertEquals("testString", resultArray[0].getSimpleClass().getString()); + assertEquals(Integer.valueOf("2"), resultArray[0].getSimpleClass().getIntValue()); + } + } + } + + + public static Timestamp toTimestamp(ZonedDateTime dateTime) { + return new Timestamp(dateTime.toInstant().getEpochSecond() * 1000L); + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayNoBinds() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "insert into array_of_integers select ([1, 2, 3]::array(integer));"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(INTEGER))"); + + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { + resultSet.next(); + Long[] resultArray = (Long[]) resultSet.getArray(1).getArray(); + assertEquals(Long.valueOf(1), resultArray[0]); + assertEquals(Long.valueOf(2), resultArray[1]); + assertEquals(Long.valueOf(3), resultArray[2]); + } + } + } + +} diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java new file mode 100644 index 000000000..26e15a540 --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2012-2024 Snowflake Computing Inc. All right reserved. + */ +package net.snowflake.client.jdbc; + +import net.snowflake.client.ConditionalIgnoreRule; +import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryResultSet; +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.Ignore; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.sql.Array; +import java.sql.Connection; +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.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +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; + +@RunWith(Parameterized.class) +@Category(TestCategoryResultSet.class) +public class BindingAndInsertingMapsStructuredTypesLatestIT 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 BindingAndInsertingMapsStructuredTypesLatestIT(ResultSetFormatType queryResultFormat) { + this.queryResultFormat = queryResultFormat; + } + + public Connection init() 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"); + stmt.execute("alter session set IGNORE_CLIENT_VESRION_IN_STRUCTURED_TYPES_RESPONSE = true"); + stmt.execute("alter session set ENABLE_STRUCTURED_TYPES_IN_BINDS = enable"); + stmt.execute("alter session set ENABLE_OBJECT_TYPED_BINDS = true"); + stmt.execute("alter session set enable_structured_types_in_fdn_tables=true"); + stmt.execute("ALTER SESSION SET TIMEZONE = 'Europe/Warsaw'"); + 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; + } + + @Before + public void setup() { + SnowflakeObjectTypeFactories.register(SimpleClass.class, SimpleClass::new); + SnowflakeObjectTypeFactories.register(AllTypesClass.class, AllTypesClass::new); + } + + @After + public void clean() { + SnowflakeObjectTypeFactories.unregister(SimpleClass.class); + SnowflakeObjectTypeFactories.unregister(AllTypesClass.class); + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfSqlData() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute( + " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, OBJECT(string VARCHAR, intValue INTEGER)))"); + + Map mapStruct = + Stream.of( + new Object[][] { + {"x", new SimpleClass("string1", 1)}, + {"y", new SimpleClass("string2", 2)}, + }) + .collect(Collectors.toMap(data -> (String) data[0], data -> (SimpleClass) data[1])); + + stmt.setMap(1, mapStruct, Types.STRUCT); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.STRUCT); + + try (ResultSet resultSet = stmt2.executeQuery()) { + resultSet.next(); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, SimpleClass.class); + assertEquals("string1", map.get("x").getString()); + assertEquals(Integer.valueOf(1), map.get("x").getIntValue()); + assertEquals("string2", map.get("y").getString()); + assertEquals(Integer.valueOf(2), map.get("y").getIntValue()); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfAllTypes() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute( + " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, " + + " OBJECT(string VARCHAR, " + + " b TINYINT, " + + " s SMALLINT, " + + " i INTEGER, " + + " l BIGINT, " + + " f FLOAT, " + + " d DOUBLE, " + + " bd NUMBER(38,2), " + + " bool BOOLEAN, " + + " timestampLtz TIMESTAMP_LTZ, " + + " timestampNtz TIMESTAMP_NTZ, " + + " timestampTz TIMESTAMP_TZ, " + + " date DATE," + + " time TIME, " + + " binary BINARY, " + + " simpleClass OBJECT(string VARCHAR, intValue INTEGER)" + + " )))"); + + Map mapStruct = + Stream.of( + new Object[][] { + {"x", new AllTypesClass( + "string", + "1".getBytes(StandardCharsets.UTF_8)[0], + Short.valueOf("2"), + Integer.valueOf(3), + Long.valueOf(4), + 1.1f, + 2.24, + new BigDecimal("999999999999999999999999999999999999.55"), + Boolean.TRUE, + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), + Date.valueOf("2023-12-24"), + Time.valueOf("12:34:56"), + new byte[] {'a', 'b', 'c'}, + new SimpleClass("testString", 2))}, + {"y", new AllTypesClass( + "string", + "1".getBytes(StandardCharsets.UTF_8)[0], + Short.valueOf("2"), + Integer.valueOf(3), + Long.valueOf(4), + 1.1f, + 2.24, + new BigDecimal("999999999999999999999999999999999999.55"), + Boolean.TRUE, + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), + Date.valueOf("2023-12-24"), + Time.valueOf("12:34:56"), + new byte[] {'a', 'b', 'c'}, + new SimpleClass("testString", 2))}, + }) + .collect(Collectors.toMap(data -> (String) data[0], data -> (AllTypesClass) data[1])); + + stmt.setMap(1, mapStruct, Types.STRUCT); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.STRUCT); + + try (ResultSet resultSet = stmt2.executeQuery()) { + resultSet.next(); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, AllTypesClass.class); + assertEquals("string", map.get("x").getString()); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfInteger() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, INTEGER))"); + + Map mapStruct = new HashMap<>(); + mapStruct.put("x", 1); + mapStruct.put("y", 2); +// mapStruct.put("z", null); + + stmt.setMap(1, mapStruct, Types.INTEGER); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.INTEGER); + + try (ResultSet resultSet = stmt2.executeQuery()) { + resultSet.next(); + Map resultMap = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Integer.class); + assertEquals(Integer.valueOf(1), resultMap.get("x")); + assertEquals(Integer.valueOf(2), resultMap.get("y")); +// assertNull(resultMap.get("z")); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfTimestamp() throws SQLException { + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_LTZ))"); + + Map mapStruct = new HashMap<>(); + mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); + mapStruct.put("y", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45))); + + stmt.setMap(1, mapStruct, Types.TIMESTAMP); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.TIMESTAMP); + + try (ResultSet resultSet = stmt2.executeQuery()) { + resultSet.next(); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Timestamp.class); + assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), map.get("x")); + assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)), map.get("y")); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfTime() throws SQLException { + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIME))"); + + Map mapStruct = new HashMap<>(); + mapStruct.put("x", Time.valueOf("12:34:56")); + mapStruct.put("y", Time.valueOf("12:34:57")); + + stmt.setMap(1, mapStruct, Types.TIME); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.TIME); + + try (ResultSet resultSet = stmt2.executeQuery()) { + resultSet.next(); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Time.class); + assertEquals(Time.valueOf("12:34:56"), map.get("x")); + assertEquals(Time.valueOf("12:34:57"), map.get("y")); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfDate() throws SQLException { + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, DATE))"); + + Map mapStruct = new HashMap<>(); + mapStruct.put("x", Date.valueOf("2023-12-24")); + mapStruct.put("y", Date.valueOf("2023-12-25")); + + stmt.setMap(1, mapStruct, Types.DATE); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.DATE); + + try (ResultSet resultSet = stmt2.executeQuery()) { + resultSet.next(); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Date.class); + assertEquals(Date.valueOf("2023-12-24"), map.get("x")); + assertEquals(Date.valueOf("2023-12-25"), map.get("y")); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfBinary() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, BINARY))"); + + Map mapStruct = new HashMap<>(); + mapStruct.put("x", new byte[] {'a', 'b', 'c'}); + mapStruct.put("y", new byte[] {'d', 'e', 'f'}); + + stmt.setMap(1, mapStruct, Types.BINARY); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.BINARY); + + try (ResultSet resultSet = stmt2.executeQuery()) { + resultSet.next(); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, byte[].class); + + assertArrayEquals(new byte[] {'a', 'b', 'c'}, map.get("x")); + assertArrayEquals(new byte[] {'d', 'e', 'f'}, map.get("y")); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapWithNulls() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, VARCHAR))"); + + Map mapStruct = new HashMap<>(); + mapStruct.put("x", null); + mapStruct.put("y", "abc"); + + stmt.setMap(1, mapStruct, Types.VARCHAR); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.VARCHAR); + + try (ResultSet resultSet = stmt2.executeQuery()) { + resultSet.next(); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, String.class); + assertNull(map.get("x")); + assertEquals("abc", map.get("y")); + } + } + } + + private static Timestamp toTimestamp(ZonedDateTime dateTime) { + return new Timestamp(dateTime.toInstant().getEpochSecond() * 1000L); + } +} diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java index a408e5d5a..e6dc47528 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java @@ -39,6 +39,7 @@ import org.junit.After; import org.junit.Assume; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -267,125 +268,8 @@ public void testWriteObjectAllTypes() throws SQLException { } } - public static Timestamp toTimestamp(ZonedDateTime dateTime) { + private 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(); - Statement statement = connection.createStatement(); - SnowflakePreparedStatementV1 stmt = - (SnowflakePreparedStatementV1) - connection.prepareStatement( - "INSERT INTO array_of_integers (arrayInt) SELECT ?;"); ) { - - statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(INTEGER))"); - - Array array = connection.createArrayOf("INTEGER", new Integer[] {1, 2, 3}); - stmt.setArray(1, array); - stmt.executeUpdate(); - - try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { - resultSet.next(); - - Long[] resultArray = (Long[]) resultSet.getArray(1).getArray(); - assertEquals(Long.valueOf(1), resultArray[0]); - assertEquals(Long.valueOf(2), resultArray[1]); - assertEquals(Long.valueOf(3), resultArray[2]); - } - } - } - - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteArrayNoBinds() throws SQLException { - try (Connection connection = init(); - Statement statement = connection.createStatement(); - SnowflakePreparedStatementV1 stmt = - (SnowflakePreparedStatementV1) - connection.prepareStatement( - "insert into array_of_integers select ([1, 2, 3]::array(integer));"); ) { - - statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(INTEGER))"); - - stmt.executeUpdate(); - - try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { - resultSet.next(); - Long[] resultArray = (Long[]) resultSet.getArray(1).getArray(); - assertEquals(Long.valueOf(1), resultArray[0]); - assertEquals(Long.valueOf(2), resultArray[1]); - assertEquals(Long.valueOf(3), resultArray[2]); - } - } - } - - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteMapOfSqlData() throws SQLException { - try (Connection connection = init(); - Statement statement = connection.createStatement(); - SnowflakePreparedStatementV1 stmt = - (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); - SnowflakePreparedStatementV1 stmt2 = - (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { - - statement.execute( - " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, OBJECT(string VARCHAR, intValue INTEGER)))"); - - Map mapStruct = - Stream.of( - new Object[][] { - {"x", new SimpleClass("string1", 1)}, - {"y", new SimpleClass("string2", 2)}, - }) - .collect(Collectors.toMap(data -> (String) data[0], data -> (SimpleClass) data[1])); - - stmt.setMap(1, mapStruct, Types.STRUCT); - stmt.executeUpdate(); - - stmt2.setMap(1, mapStruct, Types.STRUCT); - - try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); - Map map = - resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, SimpleClass.class); - } - } - } - - @Test - @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteMapOfInteger() throws SQLException { - try (Connection connection = init(); - Statement statement = connection.createStatement(); - SnowflakePreparedStatementV1 stmt = - (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); - SnowflakePreparedStatementV1 stmt2 = - (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { - - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, INTEGER))"); - - Map mapStruct = new HashMap<>(); - mapStruct.put("x", 1); - mapStruct.put("y", 2); - - stmt.setMap(1, mapStruct, Types.INTEGER); - stmt.executeUpdate(); - - stmt2.setMap(1, mapStruct, Types.INTEGER); - - try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); - Map map = - resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Integer.class); - } - } - } } From 7ebbaee0112893ba16d5b5bd82b0fc5b19008248 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 24 Jul 2024 01:36:00 +0200 Subject: [PATCH 2/7] SNOW-XXXXXX - structured types bindings test all types --- .../client/core/FieldSchemaCreator.java | 10 +- .../snowflake/client/core/JsonSqlOutput.java | 14 +-- .../client/core/SFArrowResultSet.java | 5 +- .../client/core/SFBaseResultSet.java | 18 ++- .../client/core/SFJsonResultSet.java | 2 +- .../net/snowflake/client/core/SfSqlArray.java | 26 +++-- .../client/core/arrow/MapConverter.java | 4 +- .../client/core/json/Converters.java | 3 + .../client/jdbc/SnowflakeBaseResultSet.java | 7 +- .../jdbc/SnowflakePreparedStatement.java | 24 ++++ .../jdbc/SnowflakePreparedStatementV1.java | 74 ++++++++---- .../snowflake/client/jdbc/SnowflakeUtil.java | 34 ++++-- ...nsertingArraysStructuredTypesLatestIT.java | 70 +++++++++-- ...dInsertingMapsStructuredTypesLatestIT.java | 110 +++++++++++++++--- ...ngAndInsertingStructuredTypesLatestIT.java | 6 +- 15 files changed, 312 insertions(+), 95 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java b/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java index edbad9643..35740aaa3 100644 --- a/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java +++ b/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java @@ -58,10 +58,14 @@ public static BindingParameterMetadata buildSchemaWithScaleAndPrecision( public static BindingParameterMetadata buildBindingSchemaForType(int baseType) throws SQLException { - return buildBindingSchemaForType(baseType, true); + return buildBindingSchemaForType(baseType, true, null); } - public static BindingParameterMetadata buildBindingSchemaForType(int baseType, boolean addName) + public static BindingParameterMetadata buildBindingSchemaForType(int baseType, boolean addName) throws SQLException { + return buildBindingSchemaForType(baseType, addName, null); + } + + public static BindingParameterMetadata buildBindingSchemaForType(int baseType, boolean addName, String typeName) throws SQLException { String name = addName ? SnowflakeType.javaTypeToSFType(baseType, null).name() : null; switch (baseType) { @@ -86,7 +90,7 @@ public static BindingParameterMetadata buildBindingSchemaForType(int baseType, b return FieldSchemaCreator.buildSchemaTypeAndNameOnly(name, "date", Optional.empty()); case Types.TIMESTAMP: return FieldSchemaCreator.buildSchemaWithScaleAndPrecision( - name, "timestamp", 9, 0, Optional.empty()); + name, Optional.ofNullable(typeName).orElse("timestamp"), 9, 0, Optional.empty()); case Types.TIME: return FieldSchemaCreator.buildSchemaWithScaleAndPrecision( name, "time", 9, 0, Optional.empty()); diff --git a/src/main/java/net/snowflake/client/core/JsonSqlOutput.java b/src/main/java/net/snowflake/client/core/JsonSqlOutput.java index f3fb4c06c..ec6a50bb6 100644 --- a/src/main/java/net/snowflake/client/core/JsonSqlOutput.java +++ b/src/main/java/net/snowflake/client/core/JsonSqlOutput.java @@ -230,7 +230,7 @@ public void writeTimestamp(Timestamp value) throws SQLException { .filter(str -> !str.isEmpty()) .orElse(timestampSessionType)); int columnType = snowflakeTypeToJavaType(snowflakeType); - TimeZone timeZone = timeZoneDependOnType(snowflakeType, session, null); + TimeZone timeZone = SnowflakeUtil.timeZoneDependOnType(snowflakeType, session, null); String timestampAsString = SnowflakeUtil.mapSFExceptionToSQLException( () -> @@ -374,18 +374,6 @@ public BindingParameterMetadata getSchema() { return schema; } - private TimeZone timeZoneDependOnType( - SnowflakeType snowflakeType, SFBaseSession session, TimeZone tz) { - if (snowflakeType == SnowflakeType.TIMESTAMP_NTZ) { - return null; - } else if (snowflakeType == SnowflakeType.TIMESTAMP_LTZ) { - return getSessionTimezone(session); - } else if (snowflakeType == SnowflakeType.TIMESTAMP_TZ) { - return Optional.ofNullable(tz).orElse(sessionTimezone); - } - return TimeZone.getDefault(); - } - private int snowflakeTypeToJavaType(SnowflakeType snowflakeType) { if (snowflakeType == SnowflakeType.TIMESTAMP_NTZ) { return SnowflakeUtil.EXTRA_TYPES_TIMESTAMP_NTZ; diff --git a/src/main/java/net/snowflake/client/core/SFArrowResultSet.java b/src/main/java/net/snowflake/client/core/SFArrowResultSet.java index 14f21e8d1..628fa47c2 100644 --- a/src/main/java/net/snowflake/client/core/SFArrowResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFArrowResultSet.java @@ -379,11 +379,11 @@ public Converters getConverters() { @SnowflakeJdbcInternalApi public SQLInput createSqlInputForColumn( Object input, - Class parentObjectClass, + boolean isJsonMapping, int columnIndex, SFBaseSession session, List fields) { - if (parentObjectClass.equals(JsonSqlInput.class)) { + if (isJsonMapping) { return createJsonSqlInputForColumn(input, session, fields); } else { return new ArrowSqlInput((Map) input, session, converters, fields); @@ -705,6 +705,7 @@ private SfSqlArray getArrowArray(List elements, int columnIndex) throws columnSubType, mapAndConvert(elements, converters.timeFromIntConverter(scale)).toArray(Time[]::new)); case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: return new SfSqlArray( columnSubType, mapAndConvert( diff --git a/src/main/java/net/snowflake/client/core/SFBaseResultSet.java b/src/main/java/net/snowflake/client/core/SFBaseResultSet.java index add713adb..937cf166e 100644 --- a/src/main/java/net/snowflake/client/core/SFBaseResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFBaseResultSet.java @@ -236,7 +236,7 @@ public TimeZone getSessionTimeZone() { @SnowflakeJdbcInternalApi public SQLInput createSqlInputForColumn( Object input, - Class parentObjectClass, + boolean isJsonMapping, int columnIndex, SFBaseSession session, List fields) { @@ -301,27 +301,32 @@ protected SfSqlArray getJsonArray(String obj, int columnIndex) throws SFExceptio switch (columnType) { case Types.INTEGER: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().integerConverter(columnType)) .toArray(Integer[]::new)); case Types.SMALLINT: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().smallIntConverter(columnType)) .toArray(Short[]::new)); case Types.TINYINT: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().tinyIntConverter(columnType)) .toArray(Byte[]::new)); case Types.BIGINT: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().bigIntConverter(columnType)) .toArray(Long[]::new)); case Types.DECIMAL: case Types.NUMERIC: return new SfSqlArray( + data, columnSubType, convertToFixedArray( getStream(nodeElements, getConverters().bigDecimalConverter(columnType)))); @@ -329,6 +334,7 @@ protected SfSqlArray getJsonArray(String obj, int columnIndex) throws SFExceptio case Types.VARCHAR: case Types.LONGNVARCHAR: return new SfSqlArray( + data, columnSubType, getStream( nodeElements, @@ -336,32 +342,39 @@ protected SfSqlArray getJsonArray(String obj, int columnIndex) throws SFExceptio .toArray(String[]::new)); case Types.BINARY: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().bytesConverter(columnType, scale)) .toArray(Byte[][]::new)); case Types.FLOAT: case Types.REAL: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().floatConverter(columnType)) .toArray(Float[]::new)); case Types.DOUBLE: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().doubleConverter(columnType)) .toArray(Double[]::new)); case Types.DATE: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().dateStringConverter(session)) .toArray(Date[]::new)); case Types.TIME: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().timeFromStringConverter(session)) .toArray(Time[]::new)); case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: return new SfSqlArray( + data, columnSubType, getStream( nodeElements, @@ -371,16 +384,19 @@ protected SfSqlArray getJsonArray(String obj, int columnIndex) throws SFExceptio .toArray(Timestamp[]::new)); case Types.BOOLEAN: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().booleanConverter(columnType)) .toArray(Boolean[]::new)); case Types.STRUCT: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().structConverter(OBJECT_MAPPER)) .toArray(Map[]::new)); case Types.ARRAY: return new SfSqlArray( + data, columnSubType, getStream(nodeElements, getConverters().arrayConverter(OBJECT_MAPPER)) .toArray(Map[][]::new)); diff --git a/src/main/java/net/snowflake/client/core/SFJsonResultSet.java b/src/main/java/net/snowflake/client/core/SFJsonResultSet.java index 1011870df..b9bbc2093 100644 --- a/src/main/java/net/snowflake/client/core/SFJsonResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFJsonResultSet.java @@ -255,7 +255,7 @@ public Date getDate(int columnIndex, TimeZone tz) throws SFException { @SnowflakeJdbcInternalApi public SQLInput createSqlInputForColumn( Object input, - Class parentObjectClass, + boolean isJsonMapping, int columnIndex, SFBaseSession session, List fields) { diff --git a/src/main/java/net/snowflake/client/core/SfSqlArray.java b/src/main/java/net/snowflake/client/core/SfSqlArray.java index e1ba10e8b..3068ddd63 100644 --- a/src/main/java/net/snowflake/client/core/SfSqlArray.java +++ b/src/main/java/net/snowflake/client/core/SfSqlArray.java @@ -2,7 +2,6 @@ import static net.snowflake.client.core.FieldSchemaCreator.buildBindingSchemaForType; -import com.fasterxml.jackson.core.JsonProcessingException; import java.sql.Array; import java.sql.JDBCType; import java.sql.ResultSet; @@ -11,16 +10,25 @@ import java.util.Arrays; import java.util.List; import java.util.Map; + +import com.fasterxml.jackson.databind.JsonNode; import net.snowflake.client.jdbc.BindingParameterMetadata; -import net.snowflake.client.jdbc.SnowflakeUtil; @SnowflakeJdbcInternalApi public class SfSqlArray implements Array { + private JsonNode input; private int baseType; private Object elements; - public SfSqlArray(int baseType, Object elements) { + public SfSqlArray(JsonNode input, int baseType, Object elements) { + this.input = input; + this.baseType = baseType; + this.elements = elements; + } + + public SfSqlArray( int baseType, Object elements) { + this.input = null; this.baseType = baseType; this.elements = elements; } @@ -86,14 +94,6 @@ public Object getElements() { return elements; } - public String getArrayJsonString(int type) throws SQLException { - try { - return SnowflakeUtil.mapArrayElements(elements, type, null); - } catch (JsonProcessingException e) { - throw new SQLException("There is exception during array to json string.", e); - } - } - public BindingParameterMetadata getSchema() throws SQLException { return BindingParameterMetadata.BindingParameterMetadataBuilder.bindingParameterMetadata() .withType("array") @@ -107,4 +107,8 @@ public BindingParameterMetadata getSchema(List fields) .withFields(fields) .build(); } + + public JsonNode getInput() { + return input; + } } 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 2e26363cf..5b43addf2 100644 --- a/src/main/java/net/snowflake/client/core/arrow/MapConverter.java +++ b/src/main/java/net/snowflake/client/core/arrow/MapConverter.java @@ -1,6 +1,6 @@ package net.snowflake.client.core.arrow; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import net.snowflake.client.core.DataConversionContext; @@ -22,7 +22,7 @@ public MapConverter(MapVector valueVector, int columnIndex, DataConversionContex public Object toObject(int index) throws SFException { List> entriesList = (List>) vector.getObject(index); - Map converted = new HashMap<>(); + Map converted = new LinkedHashMap<>(); for (Map map : entriesList) { converted.put(map.get("key").toString(), map.get("value")); } diff --git a/src/main/java/net/snowflake/client/core/json/Converters.java b/src/main/java/net/snowflake/client/core/json/Converters.java index afe663f90..c0a3f391a 100644 --- a/src/main/java/net/snowflake/client/core/json/Converters.java +++ b/src/main/java/net/snowflake/client/core/json/Converters.java @@ -1,6 +1,7 @@ package net.snowflake.client.core.json; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.sql.Date; import java.sql.Time; @@ -11,6 +12,8 @@ import java.util.Arrays; import java.util.Map; import java.util.TimeZone; + +import net.snowflake.client.core.JsonSqlInput; import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SFException; import net.snowflake.client.core.SfTimestampUtil; diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java index 1d6eef98b..141883260 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java @@ -45,6 +45,7 @@ import net.snowflake.client.core.SFBaseResultSet; import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SFException; +import net.snowflake.client.core.SfSqlArray; import net.snowflake.client.core.structs.SQLDataCreationHelper; import net.snowflake.client.log.SFLogger; import net.snowflake.client.log.SFLoggerFactory; @@ -1455,9 +1456,10 @@ public T[] getArray(int columnIndex, Class type) throws SQLException { } else { if (SQLData.class.isAssignableFrom(type)) { SQLData instance = (SQLData) SQLDataCreationHelper.create(type); + boolean isJsonMapping = ((SfSqlArray)array).getInput() != null; SQLInput sqlInput = sfBaseResultSet.createSqlInputForColumn( - value, objects.getClass(), columnIndex, session, fieldMetadata.getFields()); + value, isJsonMapping, columnIndex, session, fieldMetadata.getFields()); instance.readSQL(sqlInput, null); arr[counter++] = (T) instance; } else if (String.class.isAssignableFrom(type)) { @@ -1605,10 +1607,11 @@ public Map getMap(int columnIndex, Class type) throws SQLExcep for (Map.Entry entry : map.entrySet()) { if (SQLData.class.isAssignableFrom(type)) { SQLData instance = (SQLData) SQLDataCreationHelper.create(type); + boolean isJsonMapping = JsonNode.class.isAssignableFrom(entry.getValue().getClass());; SQLInput sqlInput = sfBaseResultSet.createSqlInputForColumn( entry.getValue(), - object.getClass(), + isJsonMapping, columnIndex, session, valueFieldMetadata.getFields()); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java index ee3dc3ec8..d0dbf1af6 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java @@ -1,8 +1,10 @@ package net.snowflake.client.jdbc; import java.math.BigInteger; +import java.sql.Array; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; import java.util.Map; public interface SnowflakePreparedStatement { @@ -28,6 +30,20 @@ public interface SnowflakePreparedStatement { */ void setBigInteger(int parameterIndex, BigInteger x) throws SQLException; + /** + * Sets the designated parameter to the given java.sql.Array object. + * The driver converts this to an SQL ARRAY value when it + * sends it to the database. + * + * @param parameterIndex the first parameter is 1, the second is 2, ... + * @param x an Array object that maps an SQL ARRAY value + * @exception SQLException if parameterIndex does not correspond to a parameter + * marker in the SQL statement; if a database access error occurs or + * this method is called on a closed PreparedStatement + * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method + * @since 1.2 + */ + void setArray (int parameterIndex, Array x, SnowflakeType snowflakeType) throws SQLException; /** * Sets the designated parameter to the given Map instance. * @@ -36,4 +52,12 @@ public interface SnowflakePreparedStatement { * @throws SQLException */ void setMap(int parameterIndex, Map map, int type) throws SQLException; + /** + * Sets the designated parameter to the given Map instance. + * + * @param parameterIndex + * @param map + * @throws SQLException + */ + void setMap(int parameterIndex, Map map, int type, SnowflakeType snowflakeType) throws SQLException; } diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java index 77639ca55..958f9e2bf 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java @@ -414,14 +414,12 @@ private void setTimestampWithType(int parameterIndex, Timestamp x, int snowflake } public static String prepareStringOfTimestamp(Timestamp x) { - String value = - x == null - ? null - : String.valueOf( - BigDecimal.valueOf((x.getTime() - ResultUtil.msDiffJulianToGregorian(x)) / 1000) - .scaleByPowerOfTen(9) - .add(BigDecimal.valueOf(x.getNanos()))); - return value; + return x == null + ? null + : String.valueOf( + BigDecimal.valueOf((x.getTime() - ResultUtil.msDiffJulianToGregorian(x)) / 1000) + .scaleByPowerOfTen(9) + .add(BigDecimal.valueOf(x.getNanos()))); } @Override @@ -626,9 +624,10 @@ public void setClob(int parameterIndex, Clob x) throws SQLException { } @Override - public void setArray(int parameterIndex, Array array) throws SQLException { + public void setArray(int parameterIndex, Array array, SnowflakeType snowflakeType) throws SQLException { if (array instanceof SfSqlArray) { SfSqlArray sfArray = (SfSqlArray) array; + int javaType = sfArray.getBaseType(); if (sfArray.getElements() != null && SQLData.class.isAssignableFrom(sfArray.getElements().getClass().getComponentType())) { SQLData[] objects = (SQLData[]) sfArray.getElements(); SQLData sqlData = Optional.ofNullable(objects[0]).orElse(null); @@ -636,31 +635,56 @@ public void setArray(int parameterIndex, Array array) throws SQLException { sqlData.writeSQL(stream); BindingParameterMetadata valueTypeSchema = stream.getSchema(); - ParameterBindingDTO binding; - try { - binding = new ParameterBindingDTO( - "json", - SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), - SnowflakeUtil.mapArrayElements(objects, Types.STRUCT, connection), - sfArray.getSchema(Arrays.asList(valueTypeSchema))); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - parameterBindings.put(String.valueOf(parameterIndex), binding); + ParameterBindingDTO binding; + try { + binding = new ParameterBindingDTO( + "json", + SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), + SnowflakeUtil.mapArrayElements(objects, Types.STRUCT, SnowflakeType.OBJECT ,connection), + sfArray.getSchema(Arrays.asList(valueTypeSchema))); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + parameterBindings.put(String.valueOf(parameterIndex), binding); } else { + + String value = null; + try { + value = SnowflakeUtil.mapArrayElements(sfArray.getElements(), javaType, snowflakeType, connection); + } catch (JsonProcessingException e) { + throw new SQLException(e); + } + BindingParameterMetadata valueTypeSchema = FieldSchemaCreator.buildBindingSchemaForType(javaType, false, snowflakeType.name()); + ParameterBindingDTO binding = new ParameterBindingDTO( "json", SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), - sfArray.getArrayJsonString(sfArray.getBaseType()), - sfArray.getSchema()); + value, + sfArray.getSchema(Arrays.asList(valueTypeSchema))); parameterBindings.put(String.valueOf(parameterIndex), binding); } + } else { + throw new RuntimeException(); } } + @Override + public void setArray(int parameterIndex, Array array) throws SQLException { + SfSqlArray sfArray = (SfSqlArray) array; + int javaType = sfArray.getBaseType(); + SnowflakeType snowflakeType = SnowflakeType.javaTypeToSFType(javaType, connection.getSFBaseSession()); + setArray(parameterIndex, array, snowflakeType); + } + @Override public void setMap(int parameterIndex, Map map, int type) throws SQLException { + SnowflakeType.javaTypeToSFType(0, connection.getSFBaseSession()); + setMap(parameterIndex, map, type, SnowflakeType.javaTypeToSFType(0, connection.getSFBaseSession())); + } + + @Override + public void setMap(int parameterIndex, Map map, int type, SnowflakeType snowflakeType) throws SQLException { BindingParameterMetadata valueTypeSchema; if (Types.STRUCT == type) { SQLData sqlData = (SQLData) map.values().stream().findFirst().orElse(null); @@ -668,7 +692,7 @@ public void setMap(int parameterIndex, Map map, int type) throws sqlData.writeSQL(stream); valueTypeSchema = stream.getSchema(); } else { - valueTypeSchema = FieldSchemaCreator.buildBindingSchemaForType(type, false); + valueTypeSchema = FieldSchemaCreator.buildBindingSchemaForType(type, false, snowflakeType.name()); } BindingParameterMetadata schema = @@ -676,7 +700,7 @@ public void setMap(int parameterIndex, Map map, int type) throws .withType("map") .withFields( Arrays.asList( - FieldSchemaCreator.buildBindingSchemaForType(Types.VARCHAR, false), + FieldSchemaCreator.buildBindingSchemaForType(Types.VARCHAR, false, snowflakeType.name()), valueTypeSchema)) .build(); ParameterBindingDTO binding = null; @@ -685,7 +709,7 @@ public void setMap(int parameterIndex, Map map, int type) throws new ParameterBindingDTO( "json", SnowflakeUtil.javaTypeToSFTypeString(Types.STRUCT, connection.getSFBaseSession()), - SnowflakeUtil.mapMapValuesJson(map, type, connection), + SnowflakeUtil.mapMapValuesJson(map, type, snowflakeType, connection), schema); } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index 20f13f827..ee94393c3 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -104,7 +104,7 @@ public class SnowflakeUtil { public static final String BYTE_STR = "byte"; public static final String BYTES_STR = "byte array"; - public static String mapMapValuesJson(Map map, int type, SnowflakeConnectionV1 connection ) throws JsonProcessingException, SQLException { + public static String mapMapValuesJson(Map map, int type, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) throws JsonProcessingException, SQLException { if (type == Types.STRUCT) { Map jObjects = new HashMap<>(); for (Object key : map.keySet()) { @@ -134,7 +134,7 @@ public static String mapMapValuesJson(Map map, int type, SnowflakeConnectionV1 c if(map.get(key) == null) { mappedValues.put(key, null); } else { - mappedValues.put(key, formatStringForType(map.get(key), type, connection)); + mappedValues.put(key, formatStringForType(map.get(key), type, snowflakeType, connection)); } } return OBJECT_MAPPER.writeValueAsString(mappedValues); @@ -142,12 +142,12 @@ public static String mapMapValuesJson(Map map, int type, SnowflakeConnectionV1 c throw new SQLException("Unsupported type in array: " + type); } } - public static String mapArrayElements(Object ob, int type, SnowflakeConnectionV1 connection) throws JsonProcessingException, SQLException { + public static String mapArrayElements(Object ob, int type, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) throws JsonProcessingException, SQLException { if (ob == null) { return OBJECT_MAPPER.writeValueAsString(ob); } else if (type == Types.STRUCT) { SQLData[] elements = (SQLData[]) ob; - ArrayList jObjects = new ArrayList<>(); + List jObjects = new ArrayList<>(); for (SQLData element : elements) { JsonSqlOutput sqlOutput= new JsonSqlOutput(element, connection.getSFBaseSession()); element.writeSQL(sqlOutput); @@ -170,12 +170,12 @@ public static String mapArrayElements(Object ob, int type, SnowflakeConnecti Types.DATE, Types.BINARY).contains(type)) { Object[] array = (Object[]) ob; - ArrayList str = new ArrayList(); + List str = new ArrayList(); for (Object element : array) { if(element == null) { str.add(null); } else { - str.add(formatStringForType(element, type, connection)); + str.add(formatStringForType(element, type, snowflakeType, connection)); } } return OBJECT_MAPPER.writeValueAsString(str); @@ -184,7 +184,7 @@ public static String mapArrayElements(Object ob, int type, SnowflakeConnecti } } - private static String formatStringForType(Object value, int javaType, SnowflakeConnectionV1 connection) throws SQLException { + private static String formatStringForType(Object value, int javaType, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) throws SQLException { if ( value == null) { return String.valueOf(value); } else if ( javaType == Types.TIMESTAMP) { @@ -1017,4 +1017,24 @@ public static SnowflakeDateTimeFormat getFormat(SFBaseSession session, String fo return SnowflakeDateTimeFormat.fromSqlFormat( (String) session.getCommonParameters().get(format)); } + + @SnowflakeJdbcInternalApi + public static TimeZone timeZoneDependOnType( + SnowflakeType snowflakeType, SFBaseSession session, TimeZone tz) { + if (snowflakeType == SnowflakeType.TIMESTAMP_NTZ) { + return null; + } else if (snowflakeType == SnowflakeType.TIMESTAMP_LTZ) { + return getSessionTimezone(session); + } else if (snowflakeType == SnowflakeType.TIMESTAMP_TZ) { + return Optional.ofNullable(tz).orElse(getSessionTimezone(session)); + } + return TimeZone.getDefault(); + } + + @SnowflakeJdbcInternalApi + public static TimeZone getSessionTimezone(SFBaseSession sfBaseSession) { + String timeZoneName = + (String) ResultUtil.effectiveParamValue(sfBaseSession.getCommonParameters(), "TIMEZONE"); + return TimeZone.getTimeZone(timeZoneName); + } } diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java index 8907605b7..760ff091a 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java @@ -37,6 +37,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; @RunWith(Parameterized.class) @Category(TestCategoryResultSet.class) @@ -45,9 +46,9 @@ public class BindingAndInsertingArraysStructuredTypesLatestIT extends BaseJDBCTe @Parameterized.Parameters(name = "format={0}") public static Object[][] data() { return new Object[][] { -// {ResultSetFormatType.JSON}, + {ResultSetFormatType.JSON}, {ResultSetFormatType.ARROW_WITH_JSON_STRUCTURED_TYPES}, -// {ResultSetFormatType.NATIVE_ARROW} + {ResultSetFormatType.NATIVE_ARROW} }; } @@ -146,7 +147,7 @@ public void testWriteArrayString() throws SQLException { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteArrayOfTimestamp() throws SQLException { + public void testWriteArrayOfTimestampLtz() throws SQLException { try (Connection connection = init(); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = @@ -158,7 +159,7 @@ public void testWriteArrayOfTimestamp() throws SQLException { Array array = connection.createArrayOf("TIMESTAMP", new Timestamp[] {Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), - Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))}); + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45))}); stmt.setArray(1, array); stmt.executeUpdate(); @@ -168,14 +169,61 @@ public void testWriteArrayOfTimestamp() throws SQLException { Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0]); assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[1]); +// assertEquals( +// LocalDateTime.of(2021, 12, 22, 9, 43, 44) +// .atZone(ZoneId.of("Europe/Warsaw")) +// .toInstant(), +// resultArray[0].toInstant()); +// assertEquals( +// LocalDateTime.of(2021, 12, 22, 9, 43, 45) +// .atZone(ZoneId.of("Europe/Warsaw")) +// .toInstant(), +// resultArray[1].toInstant()); + + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayOfTimestampTz() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_integers (arrayInt) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(TIMESTAMP_TZ))"); + + Array array = connection.createArrayOf("TIMESTAMP", + new Timestamp[] {Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45))}); + stmt.setArray(1, array, SnowflakeType.TIMESTAMP_TZ); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { + assertTrue(resultSet.next()); + + Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 44) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + resultArray[0].toInstant()); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 45) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + resultArray[1].toInstant()); } } } - @Ignore @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteArrayOfTime() throws SQLException { + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); try (Connection connection = init(); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = @@ -191,7 +239,7 @@ public void testWriteArrayOfTime() throws SQLException { stmt.executeUpdate(); try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_time"); ) { - resultSet.next(); + assertTrue(resultSet.next()); Time[] resultArray = (Time[]) resultSet.getArray(1).getArray(); assertEquals(Time.valueOf("12:34:56"), resultArray[0]); @@ -218,7 +266,7 @@ public void testWriteArrayOfDate() throws SQLException { stmt.executeUpdate(); try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_date"); ) { - resultSet.next(); + assertTrue(resultSet.next()); Date[] resultArray = (Date[]) resultSet.getArray(1).getArray(); assertEquals(Date.valueOf("2023-12-24").toString(), resultArray[0].toString()); @@ -244,7 +292,7 @@ public void testWriteNullArray() throws SQLException { stmt.executeUpdate(); try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { - resultSet.next(); + assertTrue(resultSet.next()); Object resultArray = resultSet.getArray(1); assertNull(resultArray); @@ -270,7 +318,7 @@ public void testWriteArrayOfSqlData() throws SQLException { stmt.executeUpdate(); try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { - resultSet.next(); + assertTrue(resultSet.next()); SimpleClass[] resultArray = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, SimpleClass.class); assertEquals(Integer.valueOf(1), resultArray[0].getIntValue()); @@ -332,7 +380,7 @@ public void testWriteArrayOfAllTypesObject() throws SQLException { stmt.executeUpdate(); try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { - resultSet.next(); + assertTrue(resultSet.next()); AllTypesClass[] resultArray = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, AllTypesClass.class); System.out.println(resultArray); @@ -383,7 +431,7 @@ public void testWriteArrayNoBinds() throws SQLException { stmt.executeUpdate(); try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { - resultSet.next(); + assertTrue(resultSet.next()); Long[] resultArray = (Long[]) resultSet.getArray(1).getArray(); assertEquals(Long.valueOf(1), resultArray[0]); assertEquals(Long.valueOf(2), resultArray[1]); diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java index 26e15a540..27339a4ea 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java @@ -127,7 +127,7 @@ public void testWriteMapOfSqlData() throws SQLException { stmt2.setMap(1, mapStruct, Types.STRUCT); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, SimpleClass.class); assertEquals("string1", map.get("x").getString()); @@ -216,7 +216,7 @@ public void testWriteMapOfAllTypes() throws SQLException { stmt2.setMap(1, mapStruct, Types.STRUCT); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, AllTypesClass.class); assertEquals("string", map.get("x").getString()); @@ -249,7 +249,7 @@ public void testWriteMapOfInteger() throws SQLException { stmt2.setMap(1, mapStruct, Types.INTEGER); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); Map resultMap = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Integer.class); assertEquals(Integer.valueOf(1), resultMap.get("x")); @@ -261,8 +261,7 @@ public void testWriteMapOfInteger() throws SQLException { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) - public void testWriteMapOfTimestamp() throws SQLException { - TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); + public void testWriteMapOfTimestampLtz() throws SQLException { try (Connection connection = init(); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = @@ -278,17 +277,100 @@ public void testWriteMapOfTimestamp() throws SQLException { mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); mapStruct.put("y", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45))); - stmt.setMap(1, mapStruct, Types.TIMESTAMP); + stmt.setMap(1, mapStruct, Types.TIMESTAMP, SnowflakeType.TIMESTAMP_LTZ); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.TIMESTAMP, SnowflakeType.TIMESTAMP_LTZ); + + try (ResultSet resultSet = stmt2.executeQuery()) { + assertTrue(resultSet.next()); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Timestamp.class); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 44) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + map.get("x").toInstant()); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 45) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + map.get("y").toInstant()); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfTimestampNtz() throws SQLException { + TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_NTZ))"); + + Map mapStruct = new HashMap<>(); + mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); + mapStruct.put("y", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45))); + + stmt.setMap(1, mapStruct, Types.TIMESTAMP, SnowflakeType.TIMESTAMP_NTZ); + stmt.executeUpdate(); + + stmt2.setMap(1, mapStruct, Types.TIMESTAMP, SnowflakeType.TIMESTAMP_NTZ); + + try (ResultSet resultSet = stmt2.executeQuery()) { + assertTrue(resultSet.next()); + Map map = + resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Timestamp.class); + assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)).toInstant(), map.get("x").toInstant()); + assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)).toInstant(), map.get("y").toInstant()); + } + } + } + + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteMapOfTimestampTz() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + SnowflakePreparedStatementV1 stmt2 = + (SnowflakePreparedStatementV1) + connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + + statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_TZ))"); + + Map mapStruct = new HashMap<>(); + mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); + mapStruct.put("y", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45))); + + stmt.setMap(1, mapStruct, Types.TIMESTAMP, SnowflakeType.TIMESTAMP_TZ); stmt.executeUpdate(); - stmt2.setMap(1, mapStruct, Types.TIMESTAMP); + stmt2.setMap(1, mapStruct, Types.TIMESTAMP, SnowflakeType.TIMESTAMP_TZ); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Timestamp.class); - assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), map.get("x")); - assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)), map.get("y")); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 44) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + map.get("x").toInstant()); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 45) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + map.get("y").toInstant()); } } } @@ -318,7 +400,7 @@ public void testWriteMapOfTime() throws SQLException { stmt2.setMap(1, mapStruct, Types.TIME); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Time.class); assertEquals(Time.valueOf("12:34:56"), map.get("x")); @@ -352,7 +434,7 @@ public void testWriteMapOfDate() throws SQLException { stmt2.setMap(1, mapStruct, Types.DATE); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Date.class); assertEquals(Date.valueOf("2023-12-24"), map.get("x")); @@ -385,7 +467,7 @@ public void testWriteMapOfBinary() throws SQLException { stmt2.setMap(1, mapStruct, Types.BINARY); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, byte[].class); @@ -419,7 +501,7 @@ public void testWriteMapWithNulls() throws SQLException { stmt2.setMap(1, mapStruct, Types.VARCHAR); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, String.class); assertNull(map.get("x")); diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java index e6dc47528..a84fdfbfd 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java @@ -124,7 +124,7 @@ public void testWriteObject() throws SQLException { try (ResultSet resultSet = stmt3.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); SimpleClass object = resultSet.getObject(1, SimpleClass.class); assertEquals("text2", object.getString()); assertEquals(Integer.valueOf("3"), object.getIntValue()); @@ -175,7 +175,7 @@ public void testWriteObjectBindingNull() throws SQLException { stmt.setObject(1, null); stmt.executeUpdate(); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); SimpleClass object = resultSet.getObject(1, SimpleClass.class); assertNull(object); } @@ -239,7 +239,7 @@ public void testWriteObjectAllTypes() throws SQLException { stmt2.setObject(1, allTypeInstance); try (ResultSet resultSet = stmt2.executeQuery()) { - resultSet.next(); + assertTrue(resultSet.next()); AllTypesClass object = resultSet.getObject(1, AllTypesClass.class); assertEquals("string", object.getString()); assertEquals(49, (long) object.getB()); From 5631aa71aa972678335b9fac6b9b9f0f78ac7853 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 24 Jul 2024 01:58:12 +0200 Subject: [PATCH 3/7] SNOW-XXXXXX - structured types bindings test all types --- .../client/core/FieldSchemaCreator.java | 12 +- .../client/core/SFBaseResultSet.java | 139 ++++++------ .../net/snowflake/client/core/SfSqlArray.java | 10 +- .../client/core/json/Converters.java | 3 - .../client/jdbc/SnowflakeBaseResultSet.java | 10 +- .../jdbc/SnowflakePreparedStatement.java | 19 +- .../jdbc/SnowflakePreparedStatementV1.java | 64 ++++-- .../snowflake/client/jdbc/SnowflakeUtil.java | 92 +++++--- ...nsertingArraysStructuredTypesLatestIT.java | 213 ++++++++++-------- ...dInsertingMapsStructuredTypesLatestIT.java | 210 +++++++++-------- ...ngAndInsertingStructuredTypesLatestIT.java | 8 - 11 files changed, 419 insertions(+), 361 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java b/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java index 35740aaa3..54b631bee 100644 --- a/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java +++ b/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java @@ -61,12 +61,13 @@ public static BindingParameterMetadata buildBindingSchemaForType(int baseType) return buildBindingSchemaForType(baseType, true, null); } - public static BindingParameterMetadata buildBindingSchemaForType(int baseType, boolean addName) throws SQLException { + public static BindingParameterMetadata buildBindingSchemaForType(int baseType, boolean addName) + throws SQLException { return buildBindingSchemaForType(baseType, addName, null); } - public static BindingParameterMetadata buildBindingSchemaForType(int baseType, boolean addName, String typeName) - throws SQLException { + public static BindingParameterMetadata buildBindingSchemaForType( + int baseType, boolean addName, String typeName) throws SQLException { String name = addName ? SnowflakeType.javaTypeToSFType(baseType, null).name() : null; switch (baseType) { case Types.VARCHAR: @@ -90,13 +91,12 @@ public static BindingParameterMetadata buildBindingSchemaForType(int baseType, b return FieldSchemaCreator.buildSchemaTypeAndNameOnly(name, "date", Optional.empty()); case Types.TIMESTAMP: return FieldSchemaCreator.buildSchemaWithScaleAndPrecision( - name, Optional.ofNullable(typeName).orElse("timestamp"), 9, 0, Optional.empty()); + name, Optional.ofNullable(typeName).orElse("timestamp"), 9, 0, Optional.empty()); case Types.TIME: return FieldSchemaCreator.buildSchemaWithScaleAndPrecision( name, "time", 9, 0, Optional.empty()); case Types.BINARY: - return FieldSchemaCreator.buildSchemaForBytesType( - name, Optional.empty()); + return FieldSchemaCreator.buildSchemaForBytesType(name, Optional.empty()); default: logger.error("Could not create schema for type : " + baseType); throw new SQLException("Could not create schema for type : " + baseType); diff --git a/src/main/java/net/snowflake/client/core/SFBaseResultSet.java b/src/main/java/net/snowflake/client/core/SFBaseResultSet.java index 937cf166e..98cc7efd1 100644 --- a/src/main/java/net/snowflake/client/core/SFBaseResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFBaseResultSet.java @@ -301,114 +301,113 @@ protected SfSqlArray getJsonArray(String obj, int columnIndex) throws SFExceptio switch (columnType) { case Types.INTEGER: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().integerConverter(columnType)) - .toArray(Integer[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().integerConverter(columnType)) + .toArray(Integer[]::new)); case Types.SMALLINT: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().smallIntConverter(columnType)) - .toArray(Short[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().smallIntConverter(columnType)) + .toArray(Short[]::new)); case Types.TINYINT: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().tinyIntConverter(columnType)) - .toArray(Byte[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().tinyIntConverter(columnType)) + .toArray(Byte[]::new)); case Types.BIGINT: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().bigIntConverter(columnType)) - .toArray(Long[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().bigIntConverter(columnType)) + .toArray(Long[]::new)); case Types.DECIMAL: case Types.NUMERIC: return new SfSqlArray( - data, - columnSubType, - convertToFixedArray( - getStream(nodeElements, getConverters().bigDecimalConverter(columnType)))); + data, + columnSubType, + convertToFixedArray( + getStream(nodeElements, getConverters().bigDecimalConverter(columnType)))); case Types.CHAR: case Types.VARCHAR: case Types.LONGNVARCHAR: return new SfSqlArray( - data, - columnSubType, - getStream( - nodeElements, - getConverters().varcharConverter(columnType, columnSubType, scale)) - .toArray(String[]::new)); + data, + columnSubType, + getStream( + nodeElements, + getConverters().varcharConverter(columnType, columnSubType, scale)) + .toArray(String[]::new)); case Types.BINARY: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().bytesConverter(columnType, scale)) - .toArray(Byte[][]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().bytesConverter(columnType, scale)) + .toArray(Byte[][]::new)); case Types.FLOAT: case Types.REAL: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().floatConverter(columnType)) - .toArray(Float[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().floatConverter(columnType)) + .toArray(Float[]::new)); case Types.DOUBLE: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().doubleConverter(columnType)) - .toArray(Double[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().doubleConverter(columnType)) + .toArray(Double[]::new)); case Types.DATE: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().dateStringConverter(session)) - .toArray(Date[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().dateStringConverter(session)) + .toArray(Date[]::new)); case Types.TIME: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().timeFromStringConverter(session)) - .toArray(Time[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().timeFromStringConverter(session)) + .toArray(Time[]::new)); case Types.TIMESTAMP: case Types.TIMESTAMP_WITH_TIMEZONE: return new SfSqlArray( - data, - columnSubType, - getStream( - nodeElements, - getConverters() - .timestampFromStringConverter( - columnSubType, columnType, scale, session, null, sessionTimeZone)) - .toArray(Timestamp[]::new)); + data, + columnSubType, + getStream( + nodeElements, + getConverters() + .timestampFromStringConverter( + columnSubType, columnType, scale, session, null, sessionTimeZone)) + .toArray(Timestamp[]::new)); case Types.BOOLEAN: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().booleanConverter(columnType)) - .toArray(Boolean[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().booleanConverter(columnType)) + .toArray(Boolean[]::new)); case Types.STRUCT: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().structConverter(OBJECT_MAPPER)) - .toArray(Map[]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().structConverter(OBJECT_MAPPER)) + .toArray(Map[]::new)); case Types.ARRAY: return new SfSqlArray( - data, - columnSubType, - getStream(nodeElements, getConverters().arrayConverter(OBJECT_MAPPER)) - .toArray(Map[][]::new)); + data, + columnSubType, + getStream(nodeElements, getConverters().arrayConverter(OBJECT_MAPPER)) + .toArray(Map[][]::new)); default: throw new SFException( - ErrorCode.FEATURE_UNSUPPORTED, - "Can't construct array for data type: " + columnSubType); + ErrorCode.FEATURE_UNSUPPORTED, + "Can't construct array for data type: " + columnSubType); } } else { throw new SFException( - ErrorCode.INVALID_VALUE_CONVERT, - "Can't construct array from delivered data"); + ErrorCode.INVALID_VALUE_CONVERT, "Can't construct array from delivered data"); } } catch (JsonProcessingException e) { throw new SFException(e, ErrorCode.INVALID_STRUCT_DATA); diff --git a/src/main/java/net/snowflake/client/core/SfSqlArray.java b/src/main/java/net/snowflake/client/core/SfSqlArray.java index 3068ddd63..929ffcd85 100644 --- a/src/main/java/net/snowflake/client/core/SfSqlArray.java +++ b/src/main/java/net/snowflake/client/core/SfSqlArray.java @@ -2,6 +2,7 @@ import static net.snowflake.client.core.FieldSchemaCreator.buildBindingSchemaForType; +import com.fasterxml.jackson.databind.JsonNode; import java.sql.Array; import java.sql.JDBCType; import java.sql.ResultSet; @@ -10,8 +11,6 @@ import java.util.Arrays; import java.util.List; import java.util.Map; - -import com.fasterxml.jackson.databind.JsonNode; import net.snowflake.client.jdbc.BindingParameterMetadata; @SnowflakeJdbcInternalApi @@ -27,7 +26,7 @@ public SfSqlArray(JsonNode input, int baseType, Object elements) { this.elements = elements; } - public SfSqlArray( int baseType, Object elements) { + public SfSqlArray(int baseType, Object elements) { this.input = null; this.baseType = baseType; this.elements = elements; @@ -91,7 +90,7 @@ public ResultSet getResultSet(long index, int count, Map> map) public void free() throws SQLException {} public Object getElements() { - return elements; + return elements; } public BindingParameterMetadata getSchema() throws SQLException { @@ -101,7 +100,8 @@ public BindingParameterMetadata getSchema() throws SQLException { .build(); } - public BindingParameterMetadata getSchema(List fields) throws SQLException { + public BindingParameterMetadata getSchema(List fields) + throws SQLException { return BindingParameterMetadata.BindingParameterMetadataBuilder.bindingParameterMetadata() .withType("array") .withFields(fields) diff --git a/src/main/java/net/snowflake/client/core/json/Converters.java b/src/main/java/net/snowflake/client/core/json/Converters.java index c0a3f391a..afe663f90 100644 --- a/src/main/java/net/snowflake/client/core/json/Converters.java +++ b/src/main/java/net/snowflake/client/core/json/Converters.java @@ -1,7 +1,6 @@ package net.snowflake.client.core.json; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.sql.Date; import java.sql.Time; @@ -12,8 +11,6 @@ import java.util.Arrays; import java.util.Map; import java.util.TimeZone; - -import net.snowflake.client.core.JsonSqlInput; import net.snowflake.client.core.SFBaseSession; import net.snowflake.client.core.SFException; import net.snowflake.client.core.SfTimestampUtil; diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java index 141883260..15611fce6 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java @@ -1456,7 +1456,7 @@ public T[] getArray(int columnIndex, Class type) throws SQLException { } else { if (SQLData.class.isAssignableFrom(type)) { SQLData instance = (SQLData) SQLDataCreationHelper.create(type); - boolean isJsonMapping = ((SfSqlArray)array).getInput() != null; + boolean isJsonMapping = ((SfSqlArray) array).getInput() != null; SQLInput sqlInput = sfBaseResultSet.createSqlInputForColumn( value, isJsonMapping, columnIndex, session, fieldMetadata.getFields()); @@ -1607,7 +1607,8 @@ public Map getMap(int columnIndex, Class type) throws SQLExcep for (Map.Entry entry : map.entrySet()) { if (SQLData.class.isAssignableFrom(type)) { SQLData instance = (SQLData) SQLDataCreationHelper.create(type); - boolean isJsonMapping = JsonNode.class.isAssignableFrom(entry.getValue().getClass());; + boolean isJsonMapping = JsonNode.class.isAssignableFrom(entry.getValue().getClass()); + ; SQLInput sqlInput = sfBaseResultSet.createSqlInputForColumn( entry.getValue(), @@ -1738,7 +1739,10 @@ public Map getMap(int columnIndex, Class type) throws SQLExcep mapSFExceptionToSQLException( () -> (T) - sfBaseResultSet.getConverters().getBytesConverter().getBytes( entry.getValue(), columnType, columnSubType, scale))); + sfBaseResultSet + .getConverters() + .getBytesConverter() + .getBytes(entry.getValue(), columnType, columnSubType, scale))); } else { logger.debug( diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java index d0dbf1af6..13a303ce1 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatement.java @@ -31,19 +31,19 @@ public interface SnowflakePreparedStatement { void setBigInteger(int parameterIndex, BigInteger x) throws SQLException; /** - * Sets the designated parameter to the given java.sql.Array object. - * The driver converts this to an SQL ARRAY value when it - * sends it to the database. + * Sets the designated parameter to the given java.sql.Array object. The driver + * converts this to an SQL ARRAY value when it sends it to the database. * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x an Array object that maps an SQL ARRAY value - * @exception SQLException if parameterIndex does not correspond to a parameter - * marker in the SQL statement; if a database access error occurs or - * this method is called on a closed PreparedStatement - * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method + * @param x an SnowflakeType object that maps an Snowflake type + * @exception SQLException if parameterIndex does not correspond to a parameter marker in the SQL + * statement; if a database access error occurs or this method is called on a closed + * PreparedStatement + * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method * @since 1.2 */ - void setArray (int parameterIndex, Array x, SnowflakeType snowflakeType) throws SQLException; + void setArray(int parameterIndex, Array x, SnowflakeType snowflakeType) throws SQLException; /** * Sets the designated parameter to the given Map instance. * @@ -59,5 +59,6 @@ public interface SnowflakePreparedStatement { * @param map * @throws SQLException */ - void setMap(int parameterIndex, Map map, int type, SnowflakeType snowflakeType) throws SQLException; + void setMap(int parameterIndex, Map map, int type, SnowflakeType snowflakeType) + throws SQLException; } diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java index 958f9e2bf..1a366750c 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakePreparedStatementV1.java @@ -414,12 +414,12 @@ private void setTimestampWithType(int parameterIndex, Timestamp x, int snowflake } public static String prepareStringOfTimestamp(Timestamp x) { - return x == null - ? null - : String.valueOf( - BigDecimal.valueOf((x.getTime() - ResultUtil.msDiffJulianToGregorian(x)) / 1000) - .scaleByPowerOfTen(9) - .add(BigDecimal.valueOf(x.getNanos()))); + return x == null + ? null + : String.valueOf( + BigDecimal.valueOf((x.getTime() - ResultUtil.msDiffJulianToGregorian(x)) / 1000) + .scaleByPowerOfTen(9) + .add(BigDecimal.valueOf(x.getNanos()))); } @Override @@ -624,11 +624,13 @@ public void setClob(int parameterIndex, Clob x) throws SQLException { } @Override - public void setArray(int parameterIndex, Array array, SnowflakeType snowflakeType) throws SQLException { + public void setArray(int parameterIndex, Array array, SnowflakeType snowflakeType) + throws SQLException { if (array instanceof SfSqlArray) { SfSqlArray sfArray = (SfSqlArray) array; int javaType = sfArray.getBaseType(); - if (sfArray.getElements() != null && SQLData.class.isAssignableFrom(sfArray.getElements().getClass().getComponentType())) { + if (sfArray.getElements() != null + && SQLData.class.isAssignableFrom(sfArray.getElements().getClass().getComponentType())) { SQLData[] objects = (SQLData[]) sfArray.getElements(); SQLData sqlData = Optional.ofNullable(objects[0]).orElse(null); JsonSqlOutput stream = new JsonSqlOutput(sqlData, connection.getSFBaseSession()); @@ -637,10 +639,12 @@ public void setArray(int parameterIndex, Array array, SnowflakeType snowflakeTyp ParameterBindingDTO binding; try { - binding = new ParameterBindingDTO( + binding = + new ParameterBindingDTO( "json", SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), - SnowflakeUtil.mapArrayElements(objects, Types.STRUCT, SnowflakeType.OBJECT ,connection), + SnowflakeUtil.convertArrayToJson( + objects, Types.STRUCT, SnowflakeType.OBJECT, connection), sfArray.getSchema(Arrays.asList(valueTypeSchema))); } catch (JsonProcessingException e) { throw new RuntimeException(e); @@ -650,21 +654,24 @@ public void setArray(int parameterIndex, Array array, SnowflakeType snowflakeTyp String value = null; try { - value = SnowflakeUtil.mapArrayElements(sfArray.getElements(), javaType, snowflakeType, connection); + value = + SnowflakeUtil.convertArrayToJson( + sfArray.getElements(), javaType, snowflakeType, connection); } catch (JsonProcessingException e) { throw new SQLException(e); } - BindingParameterMetadata valueTypeSchema = FieldSchemaCreator.buildBindingSchemaForType(javaType, false, snowflakeType.name()); + BindingParameterMetadata valueTypeSchema = + FieldSchemaCreator.buildBindingSchemaForType(javaType, false, snowflakeType.name()); ParameterBindingDTO binding = - new ParameterBindingDTO( - "json", - SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), - value, - sfArray.getSchema(Arrays.asList(valueTypeSchema))); + new ParameterBindingDTO( + "json", + SnowflakeUtil.javaTypeToSFTypeString(Types.ARRAY, connection.getSFBaseSession()), + value, + sfArray.getSchema(Arrays.asList(valueTypeSchema))); parameterBindings.put(String.valueOf(parameterIndex), binding); } - } else { + } else { throw new RuntimeException(); } } @@ -673,18 +680,25 @@ public void setArray(int parameterIndex, Array array, SnowflakeType snowflakeTyp public void setArray(int parameterIndex, Array array) throws SQLException { SfSqlArray sfArray = (SfSqlArray) array; int javaType = sfArray.getBaseType(); - SnowflakeType snowflakeType = SnowflakeType.javaTypeToSFType(javaType, connection.getSFBaseSession()); + SnowflakeType snowflakeType = + SnowflakeType.javaTypeToSFType(javaType, connection.getSFBaseSession()); setArray(parameterIndex, array, snowflakeType); } @Override public void setMap(int parameterIndex, Map map, int type) throws SQLException { SnowflakeType.javaTypeToSFType(0, connection.getSFBaseSession()); - setMap(parameterIndex, map, type, SnowflakeType.javaTypeToSFType(0, connection.getSFBaseSession())); + setMap( + parameterIndex, + map, + type, + SnowflakeType.javaTypeToSFType(0, connection.getSFBaseSession())); } @Override - public void setMap(int parameterIndex, Map map, int type, SnowflakeType snowflakeType) throws SQLException { + public void setMap( + int parameterIndex, Map map, int type, SnowflakeType snowflakeType) + throws SQLException { BindingParameterMetadata valueTypeSchema; if (Types.STRUCT == type) { SQLData sqlData = (SQLData) map.values().stream().findFirst().orElse(null); @@ -692,7 +706,8 @@ public void setMap(int parameterIndex, Map map, int type, Snowfla sqlData.writeSQL(stream); valueTypeSchema = stream.getSchema(); } else { - valueTypeSchema = FieldSchemaCreator.buildBindingSchemaForType(type, false, snowflakeType.name()); + valueTypeSchema = + FieldSchemaCreator.buildBindingSchemaForType(type, false, snowflakeType.name()); } BindingParameterMetadata schema = @@ -700,7 +715,8 @@ public void setMap(int parameterIndex, Map map, int type, Snowfla .withType("map") .withFields( Arrays.asList( - FieldSchemaCreator.buildBindingSchemaForType(Types.VARCHAR, false, snowflakeType.name()), + FieldSchemaCreator.buildBindingSchemaForType( + Types.VARCHAR, false, snowflakeType.name()), valueTypeSchema)) .build(); ParameterBindingDTO binding = null; @@ -709,7 +725,7 @@ public void setMap(int parameterIndex, Map map, int type, Snowfla new ParameterBindingDTO( "json", SnowflakeUtil.javaTypeToSFTypeString(Types.STRUCT, connection.getSFBaseSession()), - SnowflakeUtil.mapMapValuesJson(map, type, snowflakeType, connection), + SnowflakeUtil.convertMapToJson(map, type, snowflakeType, connection), schema); } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index ee94393c3..4d21bde73 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -104,17 +104,25 @@ public class SnowflakeUtil { public static final String BYTE_STR = "byte"; public static final String BYTES_STR = "byte array"; - public static String mapMapValuesJson(Map map, int type, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) throws JsonProcessingException, SQLException { + public static String mapJson(Object ob) throws JsonProcessingException { + return OBJECT_MAPPER.writeValueAsString(ob); + } + + @SnowflakeJdbcInternalApi + public static String convertMapToJson( + Map map, int type, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) + throws JsonProcessingException, SQLException { if (type == Types.STRUCT) { Map jObjects = new HashMap<>(); for (Object key : map.keySet()) { SQLData element = (SQLData) map.get(key); - JsonSqlOutput sqlOutput= new JsonSqlOutput(element, connection.getSFBaseSession()); + JsonSqlOutput sqlOutput = new JsonSqlOutput(element, connection.getSFBaseSession()); element.writeSQL(sqlOutput); jObjects.put(key, sqlOutput.getJsonObject()); } return OBJECT_MAPPER.writeValueAsString(jObjects); - } else if (Arrays.asList(Types.VARCHAR, + } else if (Arrays.asList( + Types.VARCHAR, Types.CHAR, Types.FLOAT, Types.DOUBLE, @@ -128,10 +136,11 @@ public static String mapMapValuesJson(Map map, int type, SnowflakeType snowflake Types.TIMESTAMP, Types.TIME, Types.DATE, - Types.BINARY).contains(type)) { + Types.BINARY) + .contains(type)) { Map mappedValues = new HashMap<>(); for (Object key : map.keySet()) { - if(map.get(key) == null) { + if (map.get(key) == null) { mappedValues.put(key, null); } else { mappedValues.put(key, formatStringForType(map.get(key), type, snowflakeType, connection)); @@ -142,19 +151,24 @@ public static String mapMapValuesJson(Map map, int type, SnowflakeType snowflake throw new SQLException("Unsupported type in array: " + type); } } - public static String mapArrayElements(Object ob, int type, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) throws JsonProcessingException, SQLException { + + @SnowflakeJdbcInternalApi + public static String convertArrayToJson( + Object ob, int type, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) + throws JsonProcessingException, SQLException { if (ob == null) { return OBJECT_MAPPER.writeValueAsString(ob); } else if (type == Types.STRUCT) { SQLData[] elements = (SQLData[]) ob; List jObjects = new ArrayList<>(); for (SQLData element : elements) { - JsonSqlOutput sqlOutput= new JsonSqlOutput(element, connection.getSFBaseSession()); - element.writeSQL(sqlOutput); - jObjects.add(sqlOutput.getJsonObject()); + JsonSqlOutput sqlOutput = new JsonSqlOutput(element, connection.getSFBaseSession()); + element.writeSQL(sqlOutput); + jObjects.add(sqlOutput.getJsonObject()); } return OBJECT_MAPPER.writeValueAsString(jObjects); - } else if (Arrays.asList(Types.VARCHAR, + } else if (Arrays.asList( + Types.VARCHAR, Types.CHAR, Types.FLOAT, Types.DOUBLE, @@ -168,11 +182,12 @@ public static String mapArrayElements(Object ob, int type, SnowflakeType sno Types.TIMESTAMP, Types.TIME, Types.DATE, - Types.BINARY).contains(type)) { + Types.BINARY) + .contains(type)) { Object[] array = (Object[]) ob; List str = new ArrayList(); for (Object element : array) { - if(element == null) { + if (element == null) { str.add(null); } else { str.add(formatStringForType(element, type, snowflakeType, connection)); @@ -184,32 +199,35 @@ public static String mapArrayElements(Object ob, int type, SnowflakeType sno } } - private static String formatStringForType(Object value, int javaType, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) throws SQLException { - if ( value == null) { + private static String formatStringForType( + Object value, int javaType, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) + throws SQLException { + if (value == null) { return String.valueOf(value); - } else if ( javaType == Types.TIMESTAMP) { - try { - return ResultUtil.getSFTimestampAsString( - new SFTimestamp((Timestamp) value, TimeZone.getDefault()), - javaType, - 9, - getFormat(connection.getSFBaseSession(), "TIMESTAMP_NTZ_OUTPUT_FORMAT"), - getFormat(connection.getSFBaseSession(), "TIMESTAMP_LTZ_OUTPUT_FORMAT"), - getFormat(connection.getSFBaseSession(), "TIMESTAMP_TZ_OUTPUT_FORMAT"), - connection.getSFBaseSession()); - } catch (SFException e) { - throw new SQLException(e); - } - } else if ( javaType == Types.TIME) { - SnowflakeDateTimeFormat formatter = getFormat(connection.getSFBaseSession(), "TIME_OUTPUT_FORMAT"); + } else if (javaType == Types.TIMESTAMP) { + try { + return ResultUtil.getSFTimestampAsString( + new SFTimestamp((Timestamp) value, TimeZone.getDefault()), + javaType, + 9, + getFormat(connection.getSFBaseSession(), "TIMESTAMP_NTZ_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_LTZ_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_TZ_OUTPUT_FORMAT"), + connection.getSFBaseSession()); + } catch (SFException e) { + throw new SQLException(e); + } + } else if (javaType == Types.TIME) { + SnowflakeDateTimeFormat formatter = + getFormat(connection.getSFBaseSession(), "TIME_OUTPUT_FORMAT"); return formatter.format(SFTime.fromNanoseconds(((Time) value).getTime() * 1000 * 1000), 9); - } else if ( javaType == Types.DATE) { + } else if (javaType == Types.DATE) { Date date = (Date) value; return String.valueOf( - date.getTime() - + TimeZone.getDefault().getOffset(date.getTime()) - - ResultUtil.msDiffJulianToGregorian(date)); - } else if ( javaType == Types.BINARY) { + date.getTime() + + TimeZone.getDefault().getOffset(date.getTime()) + - ResultUtil.msDiffJulianToGregorian(date)); + } else if (javaType == Types.BINARY) { byte[] bytes = (byte[]) value; return new SFBinary(bytes).toHex(); } else { @@ -1015,12 +1033,12 @@ public static String getJsonNodeStringValue(JsonNode node) throws SFException { public static SnowflakeDateTimeFormat getFormat(SFBaseSession session, String format) { return SnowflakeDateTimeFormat.fromSqlFormat( - (String) session.getCommonParameters().get(format)); + (String) session.getCommonParameters().get(format)); } @SnowflakeJdbcInternalApi public static TimeZone timeZoneDependOnType( - SnowflakeType snowflakeType, SFBaseSession session, TimeZone tz) { + SnowflakeType snowflakeType, SFBaseSession session, TimeZone tz) { if (snowflakeType == SnowflakeType.TIMESTAMP_NTZ) { return null; } else if (snowflakeType == SnowflakeType.TIMESTAMP_LTZ) { @@ -1034,7 +1052,7 @@ public static TimeZone timeZoneDependOnType( @SnowflakeJdbcInternalApi public static TimeZone getSessionTimezone(SFBaseSession sfBaseSession) { String timeZoneName = - (String) ResultUtil.effectiveParamValue(sfBaseSession.getCommonParameters(), "TIMEZONE"); + (String) ResultUtil.effectiveParamValue(sfBaseSession.getCommonParameters(), "TIMEZONE"); return TimeZone.getTimeZone(timeZoneName); } } diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java index 760ff091a..c27a95bdc 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java @@ -3,19 +3,10 @@ */ package net.snowflake.client.jdbc; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryResultSet; -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.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; @@ -33,11 +24,18 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.TimeZone; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import net.snowflake.client.ConditionalIgnoreRule; +import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryResultSet; +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.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) @@ -155,11 +153,16 @@ public void testWriteArrayOfTimestampLtz() throws SQLException { connection.prepareStatement( "INSERT INTO array_of_integers (arrayInt) SELECT ?;"); ) { - statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(TIMESTAMP_LTZ))"); + statement.execute( + " CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(TIMESTAMP_LTZ))"); - Array array = connection.createArrayOf("TIMESTAMP", - new Timestamp[] {Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), - Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45))}); + Array array = + connection.createArrayOf( + "TIMESTAMP", + new Timestamp[] { + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)) + }); stmt.setArray(1, array); stmt.executeUpdate(); @@ -169,16 +172,16 @@ public void testWriteArrayOfTimestampLtz() throws SQLException { Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0]); assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[1]); -// assertEquals( -// LocalDateTime.of(2021, 12, 22, 9, 43, 44) -// .atZone(ZoneId.of("Europe/Warsaw")) -// .toInstant(), -// resultArray[0].toInstant()); -// assertEquals( -// LocalDateTime.of(2021, 12, 22, 9, 43, 45) -// .atZone(ZoneId.of("Europe/Warsaw")) -// .toInstant(), -// resultArray[1].toInstant()); + // assertEquals( + // LocalDateTime.of(2021, 12, 22, 9, 43, 44) + // .atZone(ZoneId.of("Europe/Warsaw")) + // .toInstant(), + // resultArray[0].toInstant()); + // assertEquals( + // LocalDateTime.of(2021, 12, 22, 9, 43, 45) + // .atZone(ZoneId.of("Europe/Warsaw")) + // .toInstant(), + // resultArray[1].toInstant()); } } @@ -196,9 +199,13 @@ public void testWriteArrayOfTimestampTz() throws SQLException { statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(TIMESTAMP_TZ))"); - Array array = connection.createArrayOf("TIMESTAMP", - new Timestamp[] {Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), - Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45))}); + Array array = + connection.createArrayOf( + "TIMESTAMP", + new Timestamp[] { + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)) + }); stmt.setArray(1, array, SnowflakeType.TIMESTAMP_TZ); stmt.executeUpdate(); @@ -207,15 +214,15 @@ public void testWriteArrayOfTimestampTz() throws SQLException { Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); assertEquals( - LocalDateTime.of(2021, 12, 22, 9, 43, 44) - .atZone(ZoneId.of("Europe/Warsaw")) - .toInstant(), - resultArray[0].toInstant()); + LocalDateTime.of(2021, 12, 22, 9, 43, 44) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + resultArray[0].toInstant()); assertEquals( - LocalDateTime.of(2021, 12, 22, 9, 43, 45) - .atZone(ZoneId.of("Europe/Warsaw")) - .toInstant(), - resultArray[1].toInstant()); + LocalDateTime.of(2021, 12, 22, 9, 43, 45) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + resultArray[1].toInstant()); } } } @@ -228,13 +235,13 @@ public void testWriteArrayOfTime() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement( - "INSERT INTO array_of_time (arrayType) SELECT ?;"); ) { + connection.prepareStatement("INSERT INTO array_of_time (arrayType) SELECT ?;"); ) { statement.execute(" CREATE OR REPLACE TABLE array_of_time(arrayType ARRAY(TIME))"); - Array array = connection.createArrayOf("TIME", - new Time[] {Time.valueOf("12:34:56"), Time.valueOf("12:34:57")}); + Array array = + connection.createArrayOf( + "TIME", new Time[] {Time.valueOf("12:34:56"), Time.valueOf("12:34:57")}); stmt.setArray(1, array); stmt.executeUpdate(); @@ -255,13 +262,13 @@ public void testWriteArrayOfDate() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement( - "INSERT INTO array_of_date (arrayType) SELECT ?;"); ) { + connection.prepareStatement("INSERT INTO array_of_date (arrayType) SELECT ?;"); ) { statement.execute(" CREATE OR REPLACE TABLE array_of_date(arrayType ARRAY(DATE))"); - Array array = connection.createArrayOf("DATE", - new Date[] {Date.valueOf("2023-12-24"), Date.valueOf("2023-12-24")}); + Array array = + connection.createArrayOf( + "DATE", new Date[] {Date.valueOf("2023-12-24"), Date.valueOf("2023-12-24")}); stmt.setArray(1, array); stmt.executeUpdate(); @@ -279,11 +286,11 @@ public void testWriteArrayOfDate() throws SQLException { @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteNullArray() throws SQLException { try (Connection connection = init(); - Statement statement = connection.createStatement(); - SnowflakePreparedStatementV1 stmt = - (SnowflakePreparedStatementV1) - connection.prepareStatement( - "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { statement.execute(" CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(VARCHAR))"); @@ -294,7 +301,7 @@ public void testWriteNullArray() throws SQLException { try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { assertTrue(resultSet.next()); - Object resultArray = resultSet.getArray(1); + Object resultArray = resultSet.getArray(1); assertNull(resultArray); } } @@ -304,15 +311,18 @@ public void testWriteNullArray() throws SQLException { @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteArrayOfSqlData() throws SQLException { try (Connection connection = init(); - Statement statement = connection.createStatement(); - SnowflakePreparedStatementV1 stmt = - (SnowflakePreparedStatementV1) - connection.prepareStatement( - "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { - statement.execute(" CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(OBJECT(string VARCHAR, intValue INTEGER)) )"); + statement.execute( + " CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(OBJECT(string VARCHAR, intValue INTEGER)) )"); - Array array = connection.createArrayOf("STRUCT", + Array array = + connection.createArrayOf( + "STRUCT", new SimpleClass[] {new SimpleClass("string1", 1), new SimpleClass("string12", 2)}); stmt.setArray(1, array); stmt.executeUpdate(); @@ -320,7 +330,8 @@ public void testWriteArrayOfSqlData() throws SQLException { try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { assertTrue(resultSet.next()); - SimpleClass[] resultArray = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, SimpleClass.class); + SimpleClass[] resultArray = + resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, SimpleClass.class); assertEquals(Integer.valueOf(1), resultArray[0].getIntValue()); assertEquals("string1", resultArray[0].getString()); assertEquals(Integer.valueOf(2), resultArray[1].getIntValue()); @@ -328,18 +339,20 @@ public void testWriteArrayOfSqlData() throws SQLException { } } } + @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteArrayOfAllTypesObject() throws SQLException { TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); try (Connection connection = init(); - Statement statement = connection.createStatement(); - SnowflakePreparedStatementV1 stmt = - (SnowflakePreparedStatementV1) - connection.prepareStatement( - "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { - statement.execute(" CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(OBJECT(string VARCHAR, " + statement.execute( + " CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(OBJECT(string VARCHAR, " + " b TINYINT, " + " s SMALLINT, " + " i INTEGER, " @@ -357,32 +370,37 @@ public void testWriteArrayOfAllTypesObject() throws SQLException { + " simpleClass OBJECT(string VARCHAR, intValue INTEGER)" + " ) ) )"); - Array array = connection.createArrayOf("STRUCT", + Array array = + connection.createArrayOf( + "STRUCT", new AllTypesClass[] { - new AllTypesClass( - "string", - "1".getBytes(StandardCharsets.UTF_8)[0], - Short.valueOf("2"), - Integer.valueOf(3), - Long.valueOf(4), - 1.1f, - 2.24, - new BigDecimal("999999999999999999999999999999999999.55"), - Boolean.TRUE, - Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), - toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), - toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), - Date.valueOf("2023-12-24"), - Time.valueOf("12:34:56"), - new byte[] {'a', 'b', 'c'}, - new SimpleClass("testString", 2))}); + new AllTypesClass( + "string", + "1".getBytes(StandardCharsets.UTF_8)[0], + Short.valueOf("2"), + Integer.valueOf(3), + Long.valueOf(4), + 1.1f, + 2.24, + new BigDecimal("999999999999999999999999999999999999.55"), + Boolean.TRUE, + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), + toTimestamp( + ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), + Date.valueOf("2023-12-24"), + Time.valueOf("12:34:56"), + new byte[] {'a', 'b', 'c'}, + new SimpleClass("testString", 2)) + }); stmt.setArray(1, array); stmt.executeUpdate(); try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { assertTrue(resultSet.next()); - AllTypesClass[] resultArray = resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, AllTypesClass.class); + AllTypesClass[] resultArray = + resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, AllTypesClass.class); System.out.println(resultArray); assertEquals("string", resultArray[0].getString()); assertEquals(49, (long) resultArray[0].getB()); @@ -391,15 +409,18 @@ public void testWriteArrayOfAllTypesObject() throws SQLException { assertEquals(4, (long) resultArray[0].getL()); assertEquals(1.1, (double) resultArray[0].getF(), 0.01); assertEquals(2.24, (double) resultArray[0].getD(), 0.01); - assertEquals(new BigDecimal("999999999999999999999999999999999999.55"), resultArray[0].getBd()); + assertEquals( + new BigDecimal("999999999999999999999999999999999999.55"), resultArray[0].getBd()); assertEquals(Boolean.TRUE, resultArray[0].getBool()); assertEquals( - Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0].getTimestampLtz()); + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + resultArray[0].getTimestampLtz()); assertEquals( - Timestamp.valueOf(LocalDateTime.of(2021, 12, 23, 9, 44, 44)), resultArray[0].getTimestampNtz()); + Timestamp.valueOf(LocalDateTime.of(2021, 12, 23, 9, 44, 44)), + resultArray[0].getTimestampNtz()); assertEquals( - toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), - resultArray[0].getTimestampTz()); + toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), + resultArray[0].getTimestampTz()); // TODO uncomment after merge SNOW-928973: Date field is returning one day less when getting // through getString method // assertEquals(Date.valueOf(LocalDate.of(2023, 12, 24)), resultArray[0].getDate()); @@ -411,7 +432,6 @@ public void testWriteArrayOfAllTypesObject() throws SQLException { } } - public static Timestamp toTimestamp(ZonedDateTime dateTime) { return new Timestamp(dateTime.toInstant().getEpochSecond() * 1000L); } @@ -439,5 +459,4 @@ public void testWriteArrayNoBinds() throws SQLException { } } } - } diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java index 27339a4ea..eea3e819d 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java @@ -3,24 +3,13 @@ */ package net.snowflake.client.jdbc; -import net.snowflake.client.ConditionalIgnoreRule; -import net.snowflake.client.RunningOnGithubAction; -import net.snowflake.client.category.TestCategoryResultSet; -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.Ignore; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; -import java.sql.Array; import java.sql.Connection; import java.sql.Date; import java.sql.ResultSet; @@ -30,7 +19,6 @@ import java.sql.Timestamp; import java.sql.Types; import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -39,12 +27,18 @@ import java.util.TimeZone; import java.util.stream.Collectors; import java.util.stream.Stream; - -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 net.snowflake.client.ConditionalIgnoreRule; +import net.snowflake.client.RunningOnGithubAction; +import net.snowflake.client.category.TestCategoryResultSet; +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.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) @@ -132,13 +126,13 @@ public void testWriteMapOfSqlData() throws SQLException { resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, SimpleClass.class); assertEquals("string1", map.get("x").getString()); assertEquals(Integer.valueOf(1), map.get("x").getIntValue()); - assertEquals("string2", map.get("y").getString()); - assertEquals(Integer.valueOf(2), map.get("y").getIntValue()); + assertEquals("string2", map.get("y").getString()); + assertEquals(Integer.valueOf(2), map.get("y").getIntValue()); } } } - @Test + @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteMapOfAllTypes() throws SQLException { try (Connection connection = init(); @@ -151,62 +145,74 @@ public void testWriteMapOfAllTypes() throws SQLException { connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { statement.execute( - " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, " + - " OBJECT(string VARCHAR, " - + " b TINYINT, " - + " s SMALLINT, " - + " i INTEGER, " - + " l BIGINT, " - + " f FLOAT, " - + " d DOUBLE, " - + " bd NUMBER(38,2), " - + " bool BOOLEAN, " - + " timestampLtz TIMESTAMP_LTZ, " - + " timestampNtz TIMESTAMP_NTZ, " - + " timestampTz TIMESTAMP_TZ, " - + " date DATE," - + " time TIME, " - + " binary BINARY, " - + " simpleClass OBJECT(string VARCHAR, intValue INTEGER)" - + " )))"); + " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, " + + " OBJECT(string VARCHAR, " + + " b TINYINT, " + + " s SMALLINT, " + + " i INTEGER, " + + " l BIGINT, " + + " f FLOAT, " + + " d DOUBLE, " + + " bd NUMBER(38,2), " + + " bool BOOLEAN, " + + " timestampLtz TIMESTAMP_LTZ, " + + " timestampNtz TIMESTAMP_NTZ, " + + " timestampTz TIMESTAMP_TZ, " + + " date DATE," + + " time TIME, " + + " binary BINARY, " + + " simpleClass OBJECT(string VARCHAR, intValue INTEGER)" + + " )))"); Map mapStruct = Stream.of( new Object[][] { - {"x", new AllTypesClass( - "string", - "1".getBytes(StandardCharsets.UTF_8)[0], - Short.valueOf("2"), - Integer.valueOf(3), - Long.valueOf(4), - 1.1f, - 2.24, - new BigDecimal("999999999999999999999999999999999999.55"), - Boolean.TRUE, - Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), - toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), - toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), - Date.valueOf("2023-12-24"), - Time.valueOf("12:34:56"), - new byte[] {'a', 'b', 'c'}, - new SimpleClass("testString", 2))}, - {"y", new AllTypesClass( - "string", - "1".getBytes(StandardCharsets.UTF_8)[0], - Short.valueOf("2"), - Integer.valueOf(3), - Long.valueOf(4), - 1.1f, - 2.24, - new BigDecimal("999999999999999999999999999999999999.55"), - Boolean.TRUE, - Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), - toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), - toTimestamp(ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), - Date.valueOf("2023-12-24"), - Time.valueOf("12:34:56"), - new byte[] {'a', 'b', 'c'}, - new SimpleClass("testString", 2))}, + { + "x", + new AllTypesClass( + "string", + "1".getBytes(StandardCharsets.UTF_8)[0], + Short.valueOf("2"), + Integer.valueOf(3), + Long.valueOf(4), + 1.1f, + 2.24, + new BigDecimal("999999999999999999999999999999999999.55"), + Boolean.TRUE, + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + toTimestamp( + ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), + toTimestamp( + ZonedDateTime.of( + 2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), + Date.valueOf("2023-12-24"), + Time.valueOf("12:34:56"), + new byte[] {'a', 'b', 'c'}, + new SimpleClass("testString", 2)) + }, + { + "y", + new AllTypesClass( + "string", + "1".getBytes(StandardCharsets.UTF_8)[0], + Short.valueOf("2"), + Integer.valueOf(3), + Long.valueOf(4), + 1.1f, + 2.24, + new BigDecimal("999999999999999999999999999999999999.55"), + Boolean.TRUE, + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + toTimestamp( + ZonedDateTime.of(2021, 12, 23, 9, 44, 44, 0, ZoneId.of("UTC"))), + toTimestamp( + ZonedDateTime.of( + 2021, 12, 23, 9, 44, 44, 0, ZoneId.of("Asia/Tokyo"))), + Date.valueOf("2023-12-24"), + Time.valueOf("12:34:56"), + new byte[] {'a', 'b', 'c'}, + new SimpleClass("testString", 2)) + }, }) .collect(Collectors.toMap(data -> (String) data[0], data -> (AllTypesClass) data[1])); @@ -241,7 +247,7 @@ public void testWriteMapOfInteger() throws SQLException { Map mapStruct = new HashMap<>(); mapStruct.put("x", 1); mapStruct.put("y", 2); -// mapStruct.put("z", null); + // mapStruct.put("z", null); stmt.setMap(1, mapStruct, Types.INTEGER); stmt.executeUpdate(); @@ -254,7 +260,7 @@ public void testWriteMapOfInteger() throws SQLException { resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Integer.class); assertEquals(Integer.valueOf(1), resultMap.get("x")); assertEquals(Integer.valueOf(2), resultMap.get("y")); -// assertNull(resultMap.get("z")); + // assertNull(resultMap.get("z")); } } } @@ -271,7 +277,8 @@ public void testWriteMapOfTimestampLtz() throws SQLException { (SnowflakePreparedStatementV1) connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_LTZ))"); + statement.execute( + " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_LTZ))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); @@ -287,15 +294,15 @@ public void testWriteMapOfTimestampLtz() throws SQLException { Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Timestamp.class); assertEquals( - LocalDateTime.of(2021, 12, 22, 9, 43, 44) - .atZone(ZoneId.of("Europe/Warsaw")) - .toInstant(), - map.get("x").toInstant()); + LocalDateTime.of(2021, 12, 22, 9, 43, 44) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + map.get("x").toInstant()); assertEquals( - LocalDateTime.of(2021, 12, 22, 9, 43, 45) - .atZone(ZoneId.of("Europe/Warsaw")) - .toInstant(), - map.get("y").toInstant()); + LocalDateTime.of(2021, 12, 22, 9, 43, 45) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + map.get("y").toInstant()); } } } @@ -313,7 +320,8 @@ public void testWriteMapOfTimestampNtz() throws SQLException { (SnowflakePreparedStatementV1) connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_NTZ))"); + statement.execute( + " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_NTZ))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); @@ -328,8 +336,12 @@ public void testWriteMapOfTimestampNtz() throws SQLException { assertTrue(resultSet.next()); Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Timestamp.class); - assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)).toInstant(), map.get("x").toInstant()); - assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)).toInstant(), map.get("y").toInstant()); + assertEquals( + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)).toInstant(), + map.get("x").toInstant()); + assertEquals( + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)).toInstant(), + map.get("y").toInstant()); } } } @@ -362,15 +374,15 @@ public void testWriteMapOfTimestampTz() throws SQLException { Map map = resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Timestamp.class); assertEquals( - LocalDateTime.of(2021, 12, 22, 9, 43, 44) - .atZone(ZoneId.of("Europe/Warsaw")) - .toInstant(), - map.get("x").toInstant()); + LocalDateTime.of(2021, 12, 22, 9, 43, 44) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + map.get("x").toInstant()); assertEquals( - LocalDateTime.of(2021, 12, 22, 9, 43, 45) - .atZone(ZoneId.of("Europe/Warsaw")) - .toInstant(), - map.get("y").toInstant()); + LocalDateTime.of(2021, 12, 22, 9, 43, 45) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + map.get("y").toInstant()); } } } diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java index a84fdfbfd..929a027ab 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingStructuredTypesLatestIT.java @@ -11,7 +11,6 @@ import java.math.BigDecimal; import java.nio.charset.StandardCharsets; -import java.sql.Array; import java.sql.Connection; import java.sql.Date; import java.sql.ResultSet; @@ -19,17 +18,12 @@ import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; -import java.sql.Types; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.Map; 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; @@ -39,7 +33,6 @@ import org.junit.After; import org.junit.Assume; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -271,5 +264,4 @@ public void testWriteObjectAllTypes() throws SQLException { private static Timestamp toTimestamp(ZonedDateTime dateTime) { return new Timestamp(dateTime.toInstant().getEpochSecond() * 1000L); } - } From ae00e43a4b26774cfb9d9080252280b490991e8f Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 24 Jul 2024 07:49:05 +0200 Subject: [PATCH 4/7] SNOW-XXXXXX - structured types bindings test all types --- ...nsertingArraysStructuredTypesLatestIT.java | 87 +++++++++++++------ ...dInsertingMapsStructuredTypesLatestIT.java | 54 ++++++------ 2 files changed, 86 insertions(+), 55 deletions(-) diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java index c27a95bdc..d1420808e 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java @@ -151,10 +151,10 @@ public void testWriteArrayOfTimestampLtz() throws SQLException { SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) connection.prepareStatement( - "INSERT INTO array_of_integers (arrayInt) SELECT ?;"); ) { + "INSERT INTO array_of_timestamp_ltz (arrayInt) SELECT ?;"); ) { statement.execute( - " CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(TIMESTAMP_LTZ))"); + " CREATE OR REPLACE TABLE array_of_timestamp_ltz(arrayInt ARRAY(TIMESTAMP_LTZ))"); Array array = connection.createArrayOf( @@ -166,23 +166,55 @@ public void testWriteArrayOfTimestampLtz() throws SQLException { stmt.setArray(1, array); stmt.executeUpdate(); - try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_timestamp_ltz"); ) { resultSet.next(); Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); - assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0]); - assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[1]); - // assertEquals( - // LocalDateTime.of(2021, 12, 22, 9, 43, 44) - // .atZone(ZoneId.of("Europe/Warsaw")) - // .toInstant(), - // resultArray[0].toInstant()); - // assertEquals( - // LocalDateTime.of(2021, 12, 22, 9, 43, 45) - // .atZone(ZoneId.of("Europe/Warsaw")) - // .toInstant(), - // resultArray[1].toInstant()); +// assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0]); +// assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)), resultArray[1]); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 44) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + resultArray[0].toInstant()); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 45) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + resultArray[1].toInstant()); + + } + } + } + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayOfTimestampNtz() throws SQLException { + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_timestamp_ntz (arrayInt) SELECT ?;"); ) { + + statement.execute( + " CREATE OR REPLACE TABLE array_of_timestamp_ntz(arrayInt ARRAY(TIMESTAMP_NTZ))"); + + Array array = + connection.createArrayOf( + "TIMESTAMP", + new Timestamp[] { + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)) + }); + stmt.setArray(1, array, SnowflakeType.TIMESTAMP_NTZ); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_timestamp_ntz"); ) { + resultSet.next(); + Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); + assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0]); + assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)), resultArray[1]); } } } @@ -195,9 +227,9 @@ public void testWriteArrayOfTimestampTz() throws SQLException { SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) connection.prepareStatement( - "INSERT INTO array_of_integers (arrayInt) SELECT ?;"); ) { + "INSERT INTO array_of_timestamp_tz (arrayInt) SELECT ?;"); ) { - statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(TIMESTAMP_TZ))"); + statement.execute(" CREATE OR REPLACE TABLE array_of_timestamp_tz(arrayInt ARRAY(TIMESTAMP_TZ))"); Array array = connection.createArrayOf( @@ -209,7 +241,7 @@ public void testWriteArrayOfTimestampTz() throws SQLException { stmt.setArray(1, array, SnowflakeType.TIMESTAMP_TZ); stmt.executeUpdate(); - try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_timestamp_tz"); ) { assertTrue(resultSet.next()); Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); @@ -315,10 +347,10 @@ public void testWriteArrayOfSqlData() throws SQLException { SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) connection.prepareStatement( - "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + "INSERT INTO array_of_struct (arrayType) SELECT ?;"); ) { statement.execute( - " CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(OBJECT(string VARCHAR, intValue INTEGER)) )"); + " CREATE OR REPLACE TABLE array_of_struct(arrayType ARRAY(OBJECT(string VARCHAR, intValue INTEGER)) )"); Array array = connection.createArrayOf( @@ -327,7 +359,7 @@ public void testWriteArrayOfSqlData() throws SQLException { stmt.setArray(1, array); stmt.executeUpdate(); - try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_struct"); ) { assertTrue(resultSet.next()); SimpleClass[] resultArray = @@ -343,16 +375,15 @@ public void testWriteArrayOfSqlData() throws SQLException { @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteArrayOfAllTypesObject() throws SQLException { - TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC)); try (Connection connection = init(); Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) connection.prepareStatement( - "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + "INSERT INTO array_of_all_types (arrayType) SELECT ?;"); ) { statement.execute( - " CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(OBJECT(string VARCHAR, " + " CREATE OR REPLACE TABLE array_of_all_types(arrayType ARRAY(OBJECT(string VARCHAR, " + " b TINYINT, " + " s SMALLINT, " + " i INTEGER, " @@ -396,7 +427,7 @@ public void testWriteArrayOfAllTypesObject() throws SQLException { stmt.setArray(1, array); stmt.executeUpdate(); - try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_all_types"); ) { assertTrue(resultSet.next()); AllTypesClass[] resultArray = @@ -444,13 +475,13 @@ public void testWriteArrayNoBinds() throws SQLException { SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) connection.prepareStatement( - "insert into array_of_integers select ([1, 2, 3]::array(integer));"); ) { + "insert into array_no_binds select ([1, 2, 3]::array(integer));"); ) { - statement.execute(" CREATE OR REPLACE TABLE array_of_integers(arrayInt ARRAY(INTEGER))"); + statement.execute(" CREATE OR REPLACE TABLE array_no_binds(arrayInt ARRAY(INTEGER))"); stmt.executeUpdate(); - try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_integers"); ) { + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_no_binds"); ) { assertTrue(resultSet.next()); Long[] resultArray = (Long[]) resultSet.getArray(1).getArray(); assertEquals(Long.valueOf(1), resultArray[0]); diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java index eea3e819d..cc7bba054 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java @@ -139,13 +139,13 @@ public void testWriteMapOfAllTypes() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_objects_all_types (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_objects_all_types where mapp=?"); ) { statement.execute( - " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, " + " CREATE OR REPLACE TABLE map_of_objects_all_types(mapp MAP(VARCHAR, " + " OBJECT(string VARCHAR, " + " b TINYINT, " + " s SMALLINT, " @@ -237,12 +237,12 @@ public void testWriteMapOfInteger() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_integers (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_integers where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, INTEGER))"); + statement.execute(" CREATE OR REPLACE TABLE map_of_integers(mapp MAP(VARCHAR, INTEGER))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", 1); @@ -272,13 +272,13 @@ public void testWriteMapOfTimestampLtz() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_timestamp_ltz (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_timestamp_ltz where mapp=?"); ) { statement.execute( - " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_LTZ))"); + " CREATE OR REPLACE TABLE map_of_timestamp_ltz(mapp MAP(VARCHAR, TIMESTAMP_LTZ))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); @@ -315,13 +315,13 @@ public void testWriteMapOfTimestampNtz() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_timestamp_ntz (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_timestamp_ntz where mapp=?"); ) { statement.execute( - " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_NTZ))"); + " CREATE OR REPLACE TABLE map_of_timestamp_ntz(mapp MAP(VARCHAR, TIMESTAMP_NTZ))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); @@ -353,12 +353,12 @@ public void testWriteMapOfTimestampTz() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_timestamp_tz (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_timestamp_tz where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIMESTAMP_TZ))"); + statement.execute(" CREATE OR REPLACE TABLE map_of_timestamp_tz(mapp MAP(VARCHAR, TIMESTAMP_TZ))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); @@ -395,12 +395,12 @@ public void testWriteMapOfTime() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_time (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_time where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, TIME))"); + statement.execute(" CREATE OR REPLACE TABLE map_of_time(mapp MAP(VARCHAR, TIME))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", Time.valueOf("12:34:56")); @@ -429,12 +429,12 @@ public void testWriteMapOfDate() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_date (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_date where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, DATE))"); + statement.execute(" CREATE OR REPLACE TABLE map_of_date(mapp MAP(VARCHAR, DATE))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", Date.valueOf("2023-12-24")); @@ -462,12 +462,12 @@ public void testWriteMapOfBinary() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_binary (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_binary where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, BINARY))"); + statement.execute(" CREATE OR REPLACE TABLE map_of_binary(mapp MAP(VARCHAR, BINARY))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", new byte[] {'a', 'b', 'c'}); @@ -496,12 +496,12 @@ public void testWriteMapWithNulls() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects (mapp) SELECT ?;"); + connection.prepareStatement("INSERT INTO map_of_object_with_nulls (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects where mapp=?"); ) { + connection.prepareStatement("select * from map_of_object_with_nulls where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, VARCHAR))"); + statement.execute(" CREATE OR REPLACE TABLE map_of_object_with_nulls(mapp MAP(VARCHAR, VARCHAR))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", null); From e50d4656e57d06a2c18cf37311627a98e05c4146 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 24 Jul 2024 10:08:41 +0200 Subject: [PATCH 5/7] SNOW-1449489 - schema fix --- .../client/core/FieldSchemaCreator.java | 2 +- .../snowflake/client/core/JsonSqlOutput.java | 4 + .../client/core/ParameterBindingDTO.java | 10 +- .../client/core/SFBaseResultSet.java | 4 + .../client/jdbc/SnowflakeBaseResultSet.java | 13 ++- .../snowflake/client/jdbc/SnowflakeUtil.java | 24 +++-- ...nsertingArraysStructuredTypesLatestIT.java | 93 ++++++++++++++++--- ...dInsertingMapsStructuredTypesLatestIT.java | 34 ++++--- 8 files changed, 142 insertions(+), 42 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java b/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java index 54b631bee..138570339 100644 --- a/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java +++ b/src/main/java/net/snowflake/client/core/FieldSchemaCreator.java @@ -91,7 +91,7 @@ public static BindingParameterMetadata buildBindingSchemaForType( return FieldSchemaCreator.buildSchemaTypeAndNameOnly(name, "date", Optional.empty()); case Types.TIMESTAMP: return FieldSchemaCreator.buildSchemaWithScaleAndPrecision( - name, Optional.ofNullable(typeName).orElse("timestamp"), 9, 0, Optional.empty()); + name, Optional.ofNullable(typeName).orElse("timestamp"), 0, 0, Optional.empty()); case Types.TIME: return FieldSchemaCreator.buildSchemaWithScaleAndPrecision( name, "time", 9, 0, Optional.empty()); diff --git a/src/main/java/net/snowflake/client/core/JsonSqlOutput.java b/src/main/java/net/snowflake/client/core/JsonSqlOutput.java index ec6a50bb6..869c8b8c0 100644 --- a/src/main/java/net/snowflake/client/core/JsonSqlOutput.java +++ b/src/main/java/net/snowflake/client/core/JsonSqlOutput.java @@ -28,6 +28,7 @@ import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -74,6 +75,9 @@ private TimeZone getSessionTimezone(SFBaseSession sfBaseSession) { } private static List getClassFields(SQLData original) { + if (original == null) { + return Collections.emptyList(); + } return Arrays.stream(original.getClass().getDeclaredFields()) .filter( field -> diff --git a/src/main/java/net/snowflake/client/core/ParameterBindingDTO.java b/src/main/java/net/snowflake/client/core/ParameterBindingDTO.java index 98c6690dc..a8a9fcbfe 100644 --- a/src/main/java/net/snowflake/client/core/ParameterBindingDTO.java +++ b/src/main/java/net/snowflake/client/core/ParameterBindingDTO.java @@ -4,7 +4,9 @@ package net.snowflake.client.core; +import com.fasterxml.jackson.core.JsonProcessingException; import net.snowflake.client.jdbc.BindingParameterMetadata; +import net.snowflake.client.jdbc.SnowflakeUtil; /** This class represents a binding object passed to server side Created by hyu on 6/15/17. */ public class ParameterBindingDTO { @@ -19,7 +21,13 @@ public class ParameterBindingDTO { public ParameterBindingDTO( String fmt, String type, Object value, BindingParameterMetadata schema) { - this.fmt = fmt; + System.out.println("VALUE "+value); + try { + System.out.println("SCHEMA "+ SnowflakeUtil.mapJson(schema)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + this.fmt = fmt; this.type = type; this.value = value; this.schema = schema; diff --git a/src/main/java/net/snowflake/client/core/SFBaseResultSet.java b/src/main/java/net/snowflake/client/core/SFBaseResultSet.java index 98cc7efd1..8e0ca5de9 100644 --- a/src/main/java/net/snowflake/client/core/SFBaseResultSet.java +++ b/src/main/java/net/snowflake/client/core/SFBaseResultSet.java @@ -263,7 +263,11 @@ public Timestamp convertToTimestamp( protected SQLInput createJsonSqlInputForColumn( Object input, SFBaseSession session, List fields) { JsonNode inputNode; + if (input instanceof JsonNode) { + if (((JsonNode) input).isNull()) { + return null; + } inputNode = (JsonNode) input; } else { inputNode = OBJECT_MAPPER.convertValue(input, JsonNode.class); diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java index 15611fce6..4a792f411 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeBaseResultSet.java @@ -1605,10 +1605,11 @@ public Map getMap(int columnIndex, Class type) throws SQLExcep mapSFExceptionToSQLException(() -> prepareMapWithValues(object, type)); Map resultMap = new HashMap<>(); for (Map.Entry entry : map.entrySet()) { - if (SQLData.class.isAssignableFrom(type)) { + if (entry.getValue() == null) { + resultMap.put(entry.getKey(), null); + } else if (SQLData.class.isAssignableFrom(type)) { SQLData instance = (SQLData) SQLDataCreationHelper.create(type); boolean isJsonMapping = JsonNode.class.isAssignableFrom(entry.getValue().getClass()); - ; SQLInput sqlInput = sfBaseResultSet.createSqlInputForColumn( entry.getValue(), @@ -1616,8 +1617,12 @@ public Map getMap(int columnIndex, Class type) throws SQLExcep columnIndex, session, valueFieldMetadata.getFields()); - instance.readSQL(sqlInput, null); - resultMap.put(entry.getKey(), (T) instance); + if (sqlInput != null) { + instance.readSQL(sqlInput, null); + resultMap.put(entry.getKey(), (T) instance); + } else { + resultMap.put(entry.getKey(), null); + } } else if (String.class.isAssignableFrom(type)) { resultMap.put( entry.getKey(), diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index 4d21bde73..5057d7a56 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -114,11 +114,17 @@ public static String convertMapToJson( throws JsonProcessingException, SQLException { if (type == Types.STRUCT) { Map jObjects = new HashMap<>(); + for (Object key : map.keySet()) { - SQLData element = (SQLData) map.get(key); - JsonSqlOutput sqlOutput = new JsonSqlOutput(element, connection.getSFBaseSession()); - element.writeSQL(sqlOutput); - jObjects.put(key, sqlOutput.getJsonObject()); + Object value = map.get(key); + if (value != null) { + SQLData element = (SQLData) value; + JsonSqlOutput sqlOutput = new JsonSqlOutput(element, connection.getSFBaseSession()); + element.writeSQL(sqlOutput); + jObjects.put(key, sqlOutput.getJsonObject()); + } else { + jObjects.put(key, null); + } } return OBJECT_MAPPER.writeValueAsString(jObjects); } else if (Arrays.asList( @@ -162,9 +168,13 @@ public static String convertArrayToJson( SQLData[] elements = (SQLData[]) ob; List jObjects = new ArrayList<>(); for (SQLData element : elements) { - JsonSqlOutput sqlOutput = new JsonSqlOutput(element, connection.getSFBaseSession()); - element.writeSQL(sqlOutput); - jObjects.add(sqlOutput.getJsonObject()); + if (element != null) { + JsonSqlOutput sqlOutput = new JsonSqlOutput(element, connection.getSFBaseSession()); + element.writeSQL(sqlOutput); + jObjects.add(sqlOutput.getJsonObject()); + } else { + jObjects.add(null); + } } return OBJECT_MAPPER.writeValueAsString(jObjects); } else if (Arrays.asList( diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java index d1420808e..973d14fe4 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java @@ -31,6 +31,7 @@ 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; @@ -143,6 +144,34 @@ public void testWriteArrayString() throws SQLException { } } + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayNullableString() throws SQLException { + // Nulls aren't supported for data returned in JSON format + Assume.assumeTrue(queryResultFormat == ResultSetFormatType.NATIVE_ARROW); + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_varchars (arrayType) SELECT ?;"); ) { + + statement.execute(" CREATE OR REPLACE TABLE array_of_varchars(arrayType ARRAY(VARCHAR))"); + + Array array = connection.createArrayOf("VARCHAR", new String[] {"a", null}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_varchars"); ) { + resultSet.next(); + + String[] resultArray = (String[]) resultSet.getArray(1).getArray(); + assertEquals("a", resultArray[0]); + assertNull("c", resultArray[3]); + } + } + } + @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteArrayOfTimestampLtz() throws SQLException { @@ -170,22 +199,24 @@ public void testWriteArrayOfTimestampLtz() throws SQLException { resultSet.next(); Timestamp[] resultArray = (Timestamp[]) resultSet.getArray(1).getArray(); -// assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), resultArray[0]); -// assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)), resultArray[1]); - assertEquals( - LocalDateTime.of(2021, 12, 22, 9, 43, 44) - .atZone(ZoneId.of("Europe/Warsaw")) - .toInstant(), - resultArray[0].toInstant()); - assertEquals( - LocalDateTime.of(2021, 12, 22, 9, 43, 45) - .atZone(ZoneId.of("Europe/Warsaw")) - .toInstant(), - resultArray[1].toInstant()); - + // assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)), + // resultArray[0]); + // assertEquals(Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 45)), + // resultArray[1]); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 44) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + resultArray[0].toInstant()); + assertEquals( + LocalDateTime.of(2021, 12, 22, 9, 43, 45) + .atZone(ZoneId.of("Europe/Warsaw")) + .toInstant(), + resultArray[1].toInstant()); } } } + @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteArrayOfTimestampNtz() throws SQLException { @@ -229,7 +260,8 @@ public void testWriteArrayOfTimestampTz() throws SQLException { connection.prepareStatement( "INSERT INTO array_of_timestamp_tz (arrayInt) SELECT ?;"); ) { - statement.execute(" CREATE OR REPLACE TABLE array_of_timestamp_tz(arrayInt ARRAY(TIMESTAMP_TZ))"); + statement.execute( + " CREATE OR REPLACE TABLE array_of_timestamp_tz(arrayInt ARRAY(TIMESTAMP_TZ))"); Array array = connection.createArrayOf( @@ -372,6 +404,39 @@ public void testWriteArrayOfSqlData() throws SQLException { } } + @Test + @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) + public void testWriteArrayOfNullableSqlData() throws SQLException { + // Nulls aren't supported for data returned in JSON format + Assume.assumeTrue(queryResultFormat == ResultSetFormatType.NATIVE_ARROW); + try (Connection connection = init(); + Statement statement = connection.createStatement(); + SnowflakePreparedStatementV1 stmt = + (SnowflakePreparedStatementV1) + connection.prepareStatement( + "INSERT INTO array_of_struct (arrayType) SELECT ?;"); ) { + + statement.execute( + " CREATE OR REPLACE TABLE array_of_struct(arrayType ARRAY(OBJECT(string VARCHAR, intValue INTEGER)) )"); + + Array array = + connection.createArrayOf( + "STRUCT", new SimpleClass[] {new SimpleClass("string1", 1), null}); + stmt.setArray(1, array); + stmt.executeUpdate(); + + try (ResultSet resultSet = statement.executeQuery("SELECT * from array_of_struct"); ) { + assertTrue(resultSet.next()); + + SimpleClass[] resultArray = + resultSet.unwrap(SnowflakeBaseResultSet.class).getArray(1, SimpleClass.class); + assertEquals(Integer.valueOf(1), resultArray[0].getIntValue()); + assertEquals("string1", resultArray[0].getString()); + assertNull(resultArray[1]); + } + } + } + @Test @ConditionalIgnoreRule.ConditionalIgnore(condition = RunningOnGithubAction.class) public void testWriteArrayOfAllTypesObject() throws SQLException { diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java index cc7bba054..a885daf11 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingMapsStructuredTypesLatestIT.java @@ -107,13 +107,10 @@ public void testWriteMapOfSqlData() throws SQLException { statement.execute( " CREATE OR REPLACE TABLE map_of_objects(mapp MAP(VARCHAR, OBJECT(string VARCHAR, intValue INTEGER)))"); - Map mapStruct = - Stream.of( - new Object[][] { - {"x", new SimpleClass("string1", 1)}, - {"y", new SimpleClass("string2", 2)}, - }) - .collect(Collectors.toMap(data -> (String) data[0], data -> (SimpleClass) data[1])); + Map mapStruct = new HashMap<>(); + mapStruct.put("x", new SimpleClass("string1", 1)); + mapStruct.put("y", new SimpleClass("string2", 2)); + mapStruct.put("z", null); stmt.setMap(1, mapStruct, Types.STRUCT); stmt.executeUpdate(); @@ -128,6 +125,7 @@ public void testWriteMapOfSqlData() throws SQLException { assertEquals(Integer.valueOf(1), map.get("x").getIntValue()); assertEquals("string2", map.get("y").getString()); assertEquals(Integer.valueOf(2), map.get("y").getIntValue()); + assertNull(map.get("z")); } } } @@ -139,10 +137,12 @@ public void testWriteMapOfAllTypes() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_objects_all_types (mapp) SELECT ?;"); + connection.prepareStatement( + "INSERT INTO map_of_objects_all_types (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_objects_all_types where mapp=?"); ) { + connection.prepareStatement( + "select * from map_of_objects_all_types where mapp=?"); ) { statement.execute( " CREATE OR REPLACE TABLE map_of_objects_all_types(mapp MAP(VARCHAR, " @@ -247,7 +247,7 @@ public void testWriteMapOfInteger() throws SQLException { Map mapStruct = new HashMap<>(); mapStruct.put("x", 1); mapStruct.put("y", 2); - // mapStruct.put("z", null); + mapStruct.put("z", null); stmt.setMap(1, mapStruct, Types.INTEGER); stmt.executeUpdate(); @@ -260,7 +260,7 @@ public void testWriteMapOfInteger() throws SQLException { resultSet.unwrap(SnowflakeBaseResultSet.class).getMap(1, Integer.class); assertEquals(Integer.valueOf(1), resultMap.get("x")); assertEquals(Integer.valueOf(2), resultMap.get("y")); - // assertNull(resultMap.get("z")); + assertNull(resultMap.get("z")); } } } @@ -358,7 +358,8 @@ public void testWriteMapOfTimestampTz() throws SQLException { (SnowflakePreparedStatementV1) connection.prepareStatement("select * from map_of_timestamp_tz where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_timestamp_tz(mapp MAP(VARCHAR, TIMESTAMP_TZ))"); + statement.execute( + " CREATE OR REPLACE TABLE map_of_timestamp_tz(mapp MAP(VARCHAR, TIMESTAMP_TZ))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44))); @@ -496,12 +497,15 @@ public void testWriteMapWithNulls() throws SQLException { Statement statement = connection.createStatement(); SnowflakePreparedStatementV1 stmt = (SnowflakePreparedStatementV1) - connection.prepareStatement("INSERT INTO map_of_object_with_nulls (mapp) SELECT ?;"); + connection.prepareStatement( + "INSERT INTO map_of_object_with_nulls (mapp) SELECT ?;"); SnowflakePreparedStatementV1 stmt2 = (SnowflakePreparedStatementV1) - connection.prepareStatement("select * from map_of_object_with_nulls where mapp=?"); ) { + connection.prepareStatement( + "select * from map_of_object_with_nulls where mapp=?"); ) { - statement.execute(" CREATE OR REPLACE TABLE map_of_object_with_nulls(mapp MAP(VARCHAR, VARCHAR))"); + statement.execute( + " CREATE OR REPLACE TABLE map_of_object_with_nulls(mapp MAP(VARCHAR, VARCHAR))"); Map mapStruct = new HashMap<>(); mapStruct.put("x", null); From bfe68b07bbde8343019ce1430879e730766d5a16 Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 24 Jul 2024 15:39:06 +0200 Subject: [PATCH 6/7] SNOW-1449489 - schema fix --- .../client/core/ParameterBindingDTO.java | 14 ++++++------ .../net/snowflake/client/core/ResultUtil.java | 2 +- .../snowflake/client/jdbc/SnowflakeUtil.java | 22 ++++++++++++------- ...nsertingArraysStructuredTypesLatestIT.java | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/snowflake/client/core/ParameterBindingDTO.java b/src/main/java/net/snowflake/client/core/ParameterBindingDTO.java index a8a9fcbfe..1e383b4ec 100644 --- a/src/main/java/net/snowflake/client/core/ParameterBindingDTO.java +++ b/src/main/java/net/snowflake/client/core/ParameterBindingDTO.java @@ -21,13 +21,13 @@ public class ParameterBindingDTO { public ParameterBindingDTO( String fmt, String type, Object value, BindingParameterMetadata schema) { - System.out.println("VALUE "+value); - try { - System.out.println("SCHEMA "+ SnowflakeUtil.mapJson(schema)); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - this.fmt = fmt; + System.out.println("VALUE " + value); + try { + System.out.println("SCHEMA " + SnowflakeUtil.mapJson(schema)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + this.fmt = fmt; this.type = type; this.value = value; this.schema = schema; diff --git a/src/main/java/net/snowflake/client/core/ResultUtil.java b/src/main/java/net/snowflake/client/core/ResultUtil.java index b894f4259..b8300f4af 100644 --- a/src/main/java/net/snowflake/client/core/ResultUtil.java +++ b/src/main/java/net/snowflake/client/core/ResultUtil.java @@ -293,7 +293,7 @@ public static String getSFTimestampAsString( try { Timestamp adjustedTimestamp = ResultUtil.adjustTimestamp(sfTS.getTimestamp()); - + System.out.println("Using formatter " + formatter.getSqlFormat()); return formatter.format(adjustedTimestamp, sfTS.getTimeZone(), scale); } catch (SFTimestamp.TimestampOperationNotAvailableException e) { // this timestamp doesn't fit into a Java timestamp, and therefore we diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index 5057d7a56..aa49431ba 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -212,18 +212,24 @@ public static String convertArrayToJson( private static String formatStringForType( Object value, int javaType, SnowflakeType snowflakeType, SnowflakeConnectionV1 connection) throws SQLException { + System.out.println("Value to format: " + value); if (value == null) { return String.valueOf(value); } else if (javaType == Types.TIMESTAMP) { try { - return ResultUtil.getSFTimestampAsString( - new SFTimestamp((Timestamp) value, TimeZone.getDefault()), - javaType, - 9, - getFormat(connection.getSFBaseSession(), "TIMESTAMP_NTZ_OUTPUT_FORMAT"), - getFormat(connection.getSFBaseSession(), "TIMESTAMP_LTZ_OUTPUT_FORMAT"), - getFormat(connection.getSFBaseSession(), "TIMESTAMP_TZ_OUTPUT_FORMAT"), - connection.getSFBaseSession()); + TimeZone timeZone = + SnowflakeUtil.timeZoneDependOnType(snowflakeType, connection.getSFBaseSession(), null); + String sfTimestampAsString = + ResultUtil.getSFTimestampAsString( + new SFTimestamp((Timestamp) value, timeZone), + javaType, + 9, + getFormat(connection.getSFBaseSession(), "TIMESTAMP_NTZ_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_LTZ_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_TZ_OUTPUT_FORMAT"), + connection.getSFBaseSession()); + System.out.println("Timestamp result : " + sfTimestampAsString); + return sfTimestampAsString; } catch (SFException e) { throw new SQLException(e); } diff --git a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java index 973d14fe4..7fff45771 100644 --- a/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/BindingAndInsertingArraysStructuredTypesLatestIT.java @@ -167,7 +167,7 @@ public void testWriteArrayNullableString() throws SQLException { String[] resultArray = (String[]) resultSet.getArray(1).getArray(); assertEquals("a", resultArray[0]); - assertNull("c", resultArray[3]); + assertNull(resultArray[1]); } } } From cb8ad82e9290851a3e41df949ba1e8886cc8988c Mon Sep 17 00:00:00 2001 From: Przemyslaw Motacki Date: Wed, 24 Jul 2024 23:57:55 +0200 Subject: [PATCH 7/7] SNOW-1449489 - fix empty timestamp output format --- .../java/net/snowflake/client/jdbc/SnowflakeUtil.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java index aa49431ba..3da6797e2 100644 --- a/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java +++ b/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java @@ -224,9 +224,9 @@ private static String formatStringForType( new SFTimestamp((Timestamp) value, timeZone), javaType, 9, - getFormat(connection.getSFBaseSession(), "TIMESTAMP_NTZ_OUTPUT_FORMAT"), - getFormat(connection.getSFBaseSession(), "TIMESTAMP_LTZ_OUTPUT_FORMAT"), - getFormat(connection.getSFBaseSession(), "TIMESTAMP_TZ_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_NTZ_OUTPUT_FORMAT", "TIMESTAMP_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_LTZ_OUTPUT_FORMAT", "TIMESTAMP_OUTPUT_FORMAT"), + getFormat(connection.getSFBaseSession(), "TIMESTAMP_TZ_OUTPUT_FORMAT", "TIMESTAMP_OUTPUT_FORMAT"), connection.getSFBaseSession()); System.out.println("Timestamp result : " + sfTimestampAsString); return sfTimestampAsString; @@ -1051,6 +1051,11 @@ public static SnowflakeDateTimeFormat getFormat(SFBaseSession session, String fo return SnowflakeDateTimeFormat.fromSqlFormat( (String) session.getCommonParameters().get(format)); } + public static SnowflakeDateTimeFormat getFormat(SFBaseSession session, String format, String usedIfFirstEmpty) { + String mainSqlFormat = (String) session.getCommonParameters().get(format); + String sqlFormat = !Strings.isNullOrEmpty(mainSqlFormat) ? mainSqlFormat : (String) session.getCommonParameters().get(usedIfFirstEmpty); + return SnowflakeDateTimeFormat.fromSqlFormat(sqlFormat); + } @SnowflakeJdbcInternalApi public static TimeZone timeZoneDependOnType(