Skip to content

Commit

Permalink
SNOW-1449489 - schema fix
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-pmotacki committed Jul 24, 2024
1 parent ae00e43 commit e50d465
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/net/snowflake/client/core/JsonSqlOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -74,6 +75,9 @@ private TimeZone getSessionTimezone(SFBaseSession sfBaseSession) {
}

private static List<Field> getClassFields(SQLData original) {
if (original == null) {
return Collections.emptyList();
}
return Arrays.stream(original.getClass().getDeclaredFields())
.filter(
field ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/net/snowflake/client/core/SFBaseResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,11 @@ public Timestamp convertToTimestamp(
protected SQLInput createJsonSqlInputForColumn(
Object input, SFBaseSession session, List<FieldMetadata> fields) {
JsonNode inputNode;

if (input instanceof JsonNode) {
if (((JsonNode) input).isNull()) {
return null;
}
inputNode = (JsonNode) input;
} else {
inputNode = OBJECT_MAPPER.convertValue(input, JsonNode.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1605,19 +1605,24 @@ public <T> Map<String, T> getMap(int columnIndex, Class<T> type) throws SQLExcep
mapSFExceptionToSQLException(() -> prepareMapWithValues(object, type));
Map<String, T> resultMap = new HashMap<>();
for (Map.Entry<String, Object> 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(),
isJsonMapping,
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(),
Expand Down
24 changes: 17 additions & 7 deletions src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,17 @@ public static String convertMapToJson(
throws JsonProcessingException, SQLException {
if (type == Types.STRUCT) {
Map<Object, JSONObject> 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(
Expand Down Expand Up @@ -162,9 +168,13 @@ public static String convertArrayToJson(
SQLData[] elements = (SQLData[]) ob;
List<JSONObject> 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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, SimpleClass> 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<String, SimpleClass> 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();
Expand All @@ -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"));
}
}
}
Expand All @@ -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, "
Expand Down Expand Up @@ -247,7 +247,7 @@ public void testWriteMapOfInteger() throws SQLException {
Map<String, Integer> 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();
Expand All @@ -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"));
}
}
}
Expand Down Expand Up @@ -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<String, Timestamp> mapStruct = new HashMap<>();
mapStruct.put("x", Timestamp.valueOf(LocalDateTime.of(2021, 12, 22, 9, 43, 44)));
Expand Down Expand Up @@ -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<String, String> mapStruct = new HashMap<>();
mapStruct.put("x", null);
Expand Down

0 comments on commit e50d465

Please sign in to comment.