Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -233,22 +233,18 @@ private Object convertPrimitive(String text, String type) {
case DatabricksTypeUtil.TIMESTAMP:
case DatabricksTypeUtil.TIMESTAMP_NTZ:
try {
return parseTimestamp(text);
} catch (IllegalArgumentException e) {
// Parse Arrow's expected numeric format before TimestampConverter logs a failed attempt.
// Arrow serializes TIMESTAMP/TIMESTAMP_NTZ inside nested types as epoch microseconds.
// e.g., {"ts":1696519230000000} for 2023-10-05 15:20:30 UTC
try {
long micros = Long.parseLong(text);
long seconds = Math.floorDiv(micros, 1_000_000L);
long microsRemainder = Math.floorMod(micros, 1_000_000L);
Instant instant = Instant.ofEpochSecond(seconds, microsRemainder * 1_000);
// Build from the UTC wall-clock; Timestamp.from(instant) gets re-rendered in the JVM
// default timezone, shifting nested TIMESTAMP fields (ES-1978662).
return Timestamp.valueOf(LocalDateTime.ofInstant(instant, ZoneOffset.UTC));
} catch (NumberFormatException nfe) {
LOGGER.error(e, "Failed to parse TIMESTAMP value '{}' as epoch microseconds", text);
throw e;
}
long micros = Long.parseLong(text);
long seconds = Math.floorDiv(micros, 1_000_000L);
long microsRemainder = Math.floorMod(micros, 1_000_000L);
Instant instant = Instant.ofEpochSecond(seconds, microsRemainder * 1_000);
// Build from the UTC wall-clock; Timestamp.from(instant) gets re-rendered in the JVM
// default timezone, shifting nested TIMESTAMP fields (ES-1978662).
return Timestamp.valueOf(LocalDateTime.ofInstant(instant, ZoneOffset.UTC));
} catch (NumberFormatException e) {
return parseTimestamp(text);
}
case DatabricksTypeUtil.TIME:
return Time.valueOf(text);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
package com.databricks.jdbc.api.impl;

import static org.junit.jupiter.api.Assertions.*;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;

import com.databricks.jdbc.api.internal.IDatabricksConnectionContext;
import com.databricks.jdbc.common.DatabricksClientType;
import com.databricks.jdbc.common.TelemetryLogLevel;
import com.databricks.jdbc.common.util.DatabricksThreadContextHolder;
import com.databricks.jdbc.exception.DatabricksParsingException;
import com.databricks.jdbc.telemetry.ITelemetryClient;
import com.databricks.jdbc.telemetry.TelemetryClientFactory;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

public class ComplexDataTypeParserTest {

Expand Down Expand Up @@ -257,6 +268,35 @@ void testTimestampAsEpochMicrosInStruct() throws DatabricksParsingException {
}
}

@Test
void testTimestampAsEpochMicrosDoesNotEmitFailureTelemetry() throws Exception {
String json = "{\"before_epoch\":-1,\"fractional\":1696519230123456}";
IDatabricksConnectionContext connectionContext = mock(IDatabricksConnectionContext.class);
when(connectionContext.getTelemetryLogLevel()).thenReturn(TelemetryLogLevel.DEBUG);
when(connectionContext.getConnectionUuid()).thenReturn("epoch-micros-test");
when(connectionContext.getClientType()).thenReturn(DatabricksClientType.THRIFT);
DatabricksThreadContextHolder.setConnectionContext(connectionContext);

TelemetryClientFactory factory = mock(TelemetryClientFactory.class);
ITelemetryClient client = mock(ITelemetryClient.class);

try (MockedStatic<TelemetryClientFactory> telemetryFactory =
Mockito.mockStatic(TelemetryClientFactory.class)) {
telemetryFactory.when(TelemetryClientFactory::getInstance).thenReturn(factory);
when(factory.getTelemetryClient(connectionContext)).thenReturn(client);
DatabricksStruct dbStruct =
parser.parseJsonStringToDbStruct(
json, "STRUCT<before_epoch:TIMESTAMP_NTZ,fractional:TIMESTAMP>");
Object[] attrs = dbStruct.getAttributes();

assertEquals(Timestamp.valueOf("1969-12-31 23:59:59.999999"), attrs[0]);
assertEquals(Timestamp.valueOf("2023-10-05 15:20:30.123456"), attrs[1]);
telemetryFactory.verify(TelemetryClientFactory::getInstance, never());
} finally {
DatabricksThreadContextHolder.clearAllContext();
}
}

@Test
void testTimestampAsEpochMicrosInArray() throws DatabricksParsingException {
// TIMESTAMP inside plain ARRAY — Arrow serializes as epoch microseconds
Expand Down
Loading