Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Changelog
- Upcoming release
- Added warning about using plain HTTP OAuth endpoints (snowflakedb/snowflake-jdbc#2556).
- Fix initializing ObjectMapper when DATE_OUTPUT_FORMAT is specified (snowflakedb/snowflake-jdbc#2545).

- v4.0.2
- Fix expired session token renewal when polling results (snowflakedb/snowflake-jdbc#2489)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.text.SimpleDateFormat;
import net.snowflake.client.internal.log.SFLogger;
import net.snowflake.client.internal.log.SFLoggerFactory;
import net.snowflake.common.core.SnowflakeDateTimeFormat;

/**
* Factor method used to create ObjectMapper instance. All object mapper in JDBC should be created
Expand Down Expand Up @@ -45,7 +46,10 @@ public static ObjectMapper getObjectMapperForSession(SFBaseSession session) {
// Set the mapper to use the session's object mapper settings
Object dateOutputFormat = session.getCommonParameters().get("DATE_OUTPUT_FORMAT");
if (dateOutputFormat != null) {
mapper.setDateFormat(new SimpleDateFormat(String.valueOf(dateOutputFormat)));
String dateFormat =
SnowflakeDateTimeFormat.fromSqlFormat(String.valueOf(dateOutputFormat))
.toSimpleDateTimePattern();
mapper.setDateFormat(new SimpleDateFormat(dateFormat));
} else {
log.debug("DATE_OUTPUT_FORMAT is not set in session parameters.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package net.snowflake.client.internal.core;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import net.snowflake.client.internal.jdbc.SnowflakeUtil;
import org.junit.jupiter.api.AfterAll;
Expand All @@ -18,6 +27,7 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.mockito.Mockito;

public class ObjectMapperTest {
private static final int jacksonDefaultMaxStringLength = 20_000_000;
Expand Down Expand Up @@ -81,6 +91,92 @@ public void testObjectMapperWithLargeJsonString(int lobSizeInBytes, int maxJsonS
}
}

// -------------------------------------------------------------------------
// getObjectMapper() -- config flag assertions
// -------------------------------------------------------------------------

@Test
public void testGetObjectMapper_enablesUseBigDecimalForFloats() {
ObjectMapper mapper = ObjectMapperFactory.getObjectMapper();
assertTrue(mapper.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS));
}

@Test
public void testGetObjectMapper_disablesAccessModifierOverrides() {
ObjectMapper mapper = ObjectMapperFactory.getObjectMapper();
assertFalse(mapper.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
assertFalse(mapper.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS));
}

@Test
public void testGetObjectMapper_appliesDefaultMaxStringLength() {
System.clearProperty(ObjectMapperFactory.MAX_JSON_STRING_LENGTH_JVM);
ObjectMapper mapper = ObjectMapperFactory.getObjectMapper();
assertEquals(
ObjectMapperFactory.DEFAULT_MAX_JSON_STRING_LEN,
mapper.getFactory().streamReadConstraints().getMaxStringLength());
}

// -------------------------------------------------------------------------
// getObjectMapperForSession() -- coverage of the session-aware path
// -------------------------------------------------------------------------

@Test
public void testGetObjectMapperForSession_withDateFormat_serializesDate() throws Exception {
ObjectMapper mapper = objectMapperForSession("YYYY-MM-DD");
assertEquals(
"\"2025-03-09\"", mapper.writeValueAsString(new Date(2025 - 1900, Calendar.MARCH, 9)));
}

@Test
public void testGetObjectMapperForSession_withDateFormat_deserializesDate() throws Exception {
ObjectMapper mapper = objectMapperForSession("YYYY-MM-DD");
assertEquals(
new Date(2025 - 1900, Calendar.MARCH, 9), mapper.readValue("\"2025-03-09\"", Date.class));
}

@Test
public void testGetObjectMapperForSession_withDateTimeFormat_serializesDate() throws Exception {
ObjectMapper mapper = objectMapperForSession("YYYY-MM-DD HH24:MI:SS");
assertEquals(
"\"2025-03-09 14:30:00\"",
mapper.writeValueAsString(new Date(2025 - 1900, Calendar.MARCH, 9, 14, 30, 0)));
}

@Test
public void testGetObjectMapperForSession_nullSession_returnsBaseMapper() {
ObjectMapper mapper = ObjectMapperFactory.getObjectMapperForSession(null);
assertNotNull(mapper);
assertTrue(mapper.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS));
}

@Test
public void testGetObjectMapperForSession_nullParameters_returnsBaseMapper() {
SFBaseSession session = Mockito.mock(SFBaseSession.class);
Mockito.when(session.getCommonParameters()).thenReturn(null);

ObjectMapper mapper = ObjectMapperFactory.getObjectMapperForSession(session);
assertNotNull(mapper);
assertTrue(mapper.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS));
}

@Test
public void testGetObjectMapperForSession_missingDateOutputFormat_keepsTimestampsEnabled() {
SFBaseSession session = Mockito.mock(SFBaseSession.class);
Mockito.when(session.getCommonParameters()).thenReturn(new HashMap<>());

ObjectMapper mapper = ObjectMapperFactory.getObjectMapperForSession(session);
assertTrue(mapper.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS));
}

private static ObjectMapper objectMapperForSession(String dateOutputFormat) {
SFBaseSession session = Mockito.mock(SFBaseSession.class);
Map<String, Object> params = new HashMap<>();
params.put("DATE_OUTPUT_FORMAT", dateOutputFormat);
Mockito.when(session.getCommonParameters()).thenReturn(params);
return ObjectMapperFactory.getObjectMapperForSession(session);
}

private String generateBase64EncodedJsonString(int numChar) {
StringBuilder jsonStr = new StringBuilder();
String largeStr = SnowflakeUtil.randomAlphaNumeric(numChar);
Expand Down
Loading