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 @@ -95,6 +95,14 @@ protected Object getArrayInternal(int fromIndex, int toIndexExclusive) {
LOG.finestTrace("getArrayInternal");
Class<?> targetClass = getTargetClass();
int size = toIndexExclusive - fromIndex;
if (size == 1) {
Object firstVal = getCoercedValue(fromIndex);
if (firstVal != null
&& firstVal.getClass().isArray()
&& firstVal.getClass().getComponentType().equals(targetClass)) {
return firstVal;
}
}
Object javaArray = Array.newInstance(targetClass, size);
Comment on lines +98 to 106

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This optimization for size == 1 is highly problematic and introduces a bug when the array elements themselves are arrays (for example, when targetClass is Object.class and the single element is an Object[] representing a struct, or when dealing with multi-dimensional arrays). If targetClass is Object.class and firstVal is Object[], firstVal.getClass().getComponentType().equals(targetClass) evaluates to true (Object.class == Object.class). The method will then return firstVal directly (an array of length N representing the struct's fields) instead of returning a wrapped array of length 1 containing firstVal as its single element. This violates the JDBC contract for Array.getArray(). Please remove this size == 1 shortcut.

    Object javaArray = Array.newInstance(targetClass, size);


for (int index = 0; index < size; index++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ <T> T coerceTo(Class<T> targetClass, Object value, BigQueryJdbcResultSetLogger l
return null;
}
if (coercion == null) {
if (targetClass.isAssignableFrom(sourceClass)
|| (sourceClass.isArray() && targetClass.isArray())) {
return (T) value;
}
Comment on lines +119 to +122

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The condition (sourceClass.isArray() && targetClass.isArray()) is dangerous because it allows returning value directly even when the source array type is not assignable to the target array type (e.g., trying to coerce LocalTime[] to Time[]). Since targetClass.isAssignableFrom(sourceClass) is false in such cases, casting the returned value to T will inevitably throw a ClassCastException at runtime. If the intention is to support element-wise coercion for arrays of different component types, you should implement a proper array coercion mechanism that creates a new array of the target component type and coerces each element individually. Otherwise, this condition should be removed as targetClass.isAssignableFrom(sourceClass) already safely handles assignable array types.

      if (targetClass.isAssignableFrom(sourceClass)) {
        return (T) value;
      }

if (targetClass.equals(String.class)) {
return (T) value.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,27 @@ static Timestamp convertTimestampWithCalendar(Timestamp timestamp, Calendar cal)
return adjustedTimestamp;
}

static Time localTimeToTime(LocalTime lt) {
long epochMillis =
lt.atDate(LocalDate.of(1970, 1, 1))
.atZone(ZoneId.systemDefault())
.toInstant()
.toEpochMilli();
return new Time(epochMillis);
}
Comment on lines +141 to +148

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the project's general rules, when converting or shifting timezone fields for JDBC types like java.sql.Time, legacy Calendar manipulation should be used instead of java.time APIs to ensure Daylight Saving Time (DST) is handled properly. Using lt.atDate(LocalDate.of(1970, 1, 1)).atZone(ZoneId.systemDefault()) can lead to DST or historical offset discrepancies. Please refactor this to use legacy Calendar manipulation.

  static Time localTimeToTime(LocalTime lt) {
    Calendar cal = Calendar.getInstance();
    cal.clear();
    cal.set(1970, Calendar.JANUARY, 1, lt.getHour(), lt.getMinute(), lt.getSecond());
    cal.set(Calendar.MILLISECOND, lt.getNano() / 1_000_000);
    return new Time(cal.getTimeInMillis());
  }
References
  1. When converting or shifting timezone fields for JDBC types like java.sql.Time, use legacy Calendar manipulation instead of java.time APIs to ensure Daylight Saving Time (DST) is handled properly.


static LocalDateTime parseFieldValueToLocalDateTime(FieldValue fv) {
String raw = fv.getStringValue();
if (raw.contains("T") || raw.contains(" ")) {
return LocalDateTime.parse(raw.replace(' ', 'T'));
}
long micros = fv.getTimestampValue();
return Instant.EPOCH
.plus(micros, ChronoUnit.MICROS)
.atOffset(ZoneOffset.UTC)
.toLocalDateTime();
}
Comment on lines +150 to +160

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The parseFieldValueToLocalDateTime method is highly fragile and will fail when parsing BigQuery TIMESTAMP values. If the column is a TIMESTAMP, fv.getStringValue() typically returns a string with a timezone suffix and multiple spaces (e.g., 2023-07-28 12:30:00 UTC). raw.replace(' ', 'T') replaces all spaces, resulting in 2023-07-28T12:30:00TUTC, which is invalid and will cause LocalDateTime.parse to throw a DateTimeParseException. Even if there was only one space, LocalDateTime.parse does not support timezone offsets/suffixes. Instead of checking raw.contains(" ") first, you should determine if the field is a DATETIME or a TIMESTAMP. Since DATETIME strings in BigQuery do not have timezone suffixes (and typically contain T or a single space), and TIMESTAMP values should be retrieved via fv.getTimestampValue(), you should refine this logic to avoid parsing timezone-annotated strings directly with LocalDateTime.parse.

Suggested change
static LocalDateTime parseFieldValueToLocalDateTime(FieldValue fv) {
String raw = fv.getStringValue();
if (raw.contains("T") || raw.contains(" ")) {
return LocalDateTime.parse(raw.replace(' ', 'T'));
}
long micros = fv.getTimestampValue();
return Instant.EPOCH
.plus(micros, ChronoUnit.MICROS)
.atOffset(ZoneOffset.UTC)
.toLocalDateTime();
}
static LocalDateTime parseFieldValueToLocalDateTime(FieldValue fv) {
String raw = fv.getStringValue();
if (raw.contains("T") || (raw.contains(" ") && !raw.endsWith("UTC") && !raw.endsWith("Z"))) {
return LocalDateTime.parse(raw.replace(' ', 'T'));
}
long micros = fv.getTimestampValue();
return Instant.EPOCH
.plus(micros, ChronoUnit.MICROS)
.atOffset(ZoneOffset.UTC)
.toLocalDateTime();
}


static BigQueryTypeCoercer INSTANCE;

static {
Expand All @@ -163,7 +184,7 @@ static Timestamp convertTimestampWithCalendar(Timestamp timestamp, Calendar cal)

// Read API Type coercions
.registerTypeCoercion(
(LocalDateTime ldt) -> Timestamp.from(ldt.toInstant(ZoneOffset.UTC)),
(LocalDateTime ldt) -> Timestamp.valueOf(ldt),
LocalDateTime.class,
Timestamp.class)
.registerTypeCoercion(Text::toString, Text.class, String.class)
Expand All @@ -172,58 +193,110 @@ static Timestamp convertTimestampWithCalendar(Timestamp timestamp, Calendar cal)
.registerTypeCoercion(new LongToTime())
.registerTypeCoercion(new IntegerToDate())
.registerTypeCoercion(
(Timestamp ts) ->
Date.valueOf(ts.toInstant().atOffset(ZoneOffset.UTC).toLocalDate()),
(Timestamp ts) -> Date.valueOf(ts.toLocalDateTime().toLocalDate()),
Timestamp.class,
Date.class)
.registerTypeCoercion(
(Timestamp ts) ->
Time.valueOf(ts.toInstant().atOffset(ZoneOffset.UTC).toLocalTime()),
(Timestamp ts) -> localTimeToTime(ts.toLocalDateTime().toLocalTime()),
Timestamp.class,
Time.class)
.registerTypeCoercion(
(Time time) -> // Per JDBC spec, the date component should be 1970-01-01
Timestamp.from(
LocalDateTime.of(LocalDate.ofEpochDay(0), time.toLocalTime())
.toInstant(ZoneOffset.UTC)),
Time.class,
Timestamp.class)
(Time time) -> new Timestamp(time.getTime()), Time.class, Timestamp.class)
.registerTypeCoercion(
(Date date) -> new Timestamp(date.getTime()), Date.class, Timestamp.class)
.registerTypeCoercion(
(LocalDateTime ldt) -> Date.valueOf(ldt.toLocalDate()),
LocalDateTime.class,
Date.class)
.registerTypeCoercion(
(LocalDateTime ldt) -> {
// Custom conversion is used to preserve sub-second (millisecond) precision,
// as standard java.sql.Time.valueOf(LocalTime) truncates milliseconds.
long millisOfDay = TimeUnit.NANOSECONDS.toMillis(ldt.toLocalTime().toNanoOfDay());
long localMillis = TimeZoneCache.getLocalMillis(millisOfDay);
return new Time(localMillis);
},
(LocalDateTime ldt) -> localTimeToTime(ldt.toLocalTime()),
LocalDateTime.class,
Time.class)
.registerTypeCoercion((Date date) -> date.toLocalDate(), Date.class, LocalDate.class)
.registerTypeCoercion(
(Time time) -> {
// Custom conversion is used to preserve sub-second (millisecond) precision,
// as standard java.sql.Time.toLocalTime() truncates milliseconds.
long millis = time.getTime();
long localMillis = millis + TimeZoneCache.getOffset(millis);
return LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos(localMillis));
},
(Date date) -> date.toLocalDate().atStartOfDay(),
Date.class,
LocalDateTime.class)
.registerTypeCoercion(
(Time time) ->
Instant.ofEpochMilli(time.getTime())
.atZone(ZoneId.systemDefault())
.toLocalTime(),
Time.class,
LocalTime.class)
.registerTypeCoercion(
(Timestamp ts) -> ts.toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime(),
(Timestamp ts) -> ts.toLocalDateTime(), Timestamp.class, LocalDateTime.class)
.registerTypeCoercion(
(Timestamp ts) -> ts.toLocalDateTime().toLocalDate(),
Timestamp.class,
LocalDateTime.class)
LocalDate.class)
.registerTypeCoercion(
(Timestamp ts) -> ts.toInstant().atOffset(ZoneOffset.UTC),
(Timestamp ts) -> ts.toLocalDateTime().toLocalTime(),
Timestamp.class,
LocalTime.class)
.registerTypeCoercion(
(Timestamp ts) -> ts.toLocalDateTime().atOffset(ZoneOffset.UTC),
Timestamp.class,
OffsetDateTime.class)
Comment on lines +237 to 240

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Converting Timestamp to OffsetDateTime using ts.toLocalDateTime().atOffset(ZoneOffset.UTC) is incorrect and introduces timezone shifts. ts.toLocalDateTime() converts the timestamp to the system default timezone. Attaching ZoneOffset.UTC to this local date-time without adjusting the offset will result in an incorrect instant. Please revert this to use ts.toInstant().atOffset(ZoneOffset.UTC) (or Instant.ofEpochMilli(ts.getTime()).atOffset(ZoneOffset.UTC)) to preserve the correct instant.

Suggested change
.registerTypeCoercion(
(Timestamp ts) -> ts.toLocalDateTime().atOffset(ZoneOffset.UTC),
Timestamp.class,
OffsetDateTime.class)
.registerTypeCoercion(
(Timestamp ts) -> ts.toInstant().atOffset(ZoneOffset.UTC),
Timestamp.class,
OffsetDateTime.class)

.registerTypeCoercion((Timestamp ts) -> ts.toInstant(), Timestamp.class, Instant.class)
.registerTypeCoercion(
(Timestamp ts) -> ts.toLocalDateTime().atZone(ZoneOffset.UTC),
Timestamp.class,
ZonedDateTime.class)
Comment on lines +241 to +244

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Converting Timestamp to ZonedDateTime using ts.toLocalDateTime().atZone(ZoneOffset.UTC) is incorrect and introduces timezone shifts. Please use ts.toInstant().atZone(ZoneOffset.UTC) to preserve the correct instant.

Suggested change
.registerTypeCoercion(
(Timestamp ts) -> ts.toLocalDateTime().atZone(ZoneOffset.UTC),
Timestamp.class,
ZonedDateTime.class)
.registerTypeCoercion(
(Timestamp ts) -> ts.toInstant().atZone(ZoneOffset.UTC),
Timestamp.class,
ZonedDateTime.class)

.registerTypeCoercion(
(Timestamp ts) -> ts.toLocalDateTime().atOffset(ZoneOffset.UTC).toInstant(),
Timestamp.class,
Instant.class)
Comment on lines +245 to +248

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Converting Timestamp to Instant using ts.toLocalDateTime().atOffset(ZoneOffset.UTC).toInstant() is incorrect and introduces timezone shifts. Please revert this to simply ts.toInstant().

Suggested change
.registerTypeCoercion(
(Timestamp ts) -> ts.toLocalDateTime().atOffset(ZoneOffset.UTC).toInstant(),
Timestamp.class,
Instant.class)
.registerTypeCoercion(
(Timestamp ts) -> ts.toInstant(),
Timestamp.class,
Instant.class)

.registerTypeCoercion(
(LocalDateTime ldt) -> ldt.toLocalDate(), LocalDateTime.class, LocalDate.class)
.registerTypeCoercion(
(LocalDateTime ldt) -> ldt.toLocalTime(), LocalDateTime.class, LocalTime.class)
.registerTypeCoercion(
(LocalDateTime ldt) -> ldt.atOffset(ZoneOffset.UTC),
LocalDateTime.class,
OffsetDateTime.class)
.registerTypeCoercion(
(LocalDateTime ldt) -> ldt.atZone(ZoneOffset.UTC),
LocalDateTime.class,
ZonedDateTime.class)
.registerTypeCoercion(
(LocalDateTime ldt) -> ldt.toInstant(ZoneOffset.UTC),
LocalDateTime.class,
Instant.class)
.registerTypeCoercion((LocalDate ld) -> Date.valueOf(ld), LocalDate.class, Date.class)
.registerTypeCoercion(
(LocalDate ld) -> Timestamp.valueOf(ld.atStartOfDay()),
LocalDate.class,
Timestamp.class)
.registerTypeCoercion(
(LocalDate ld) -> ld.atStartOfDay(), LocalDate.class, LocalDateTime.class)
.registerTypeCoercion(
BigQueryTypeCoercionUtility::localTimeToTime,
LocalTime.class,
Time.class)
.registerTypeCoercion(
(FieldValue fv) -> Date.valueOf(fv.getStringValue()).toLocalDate(),
FieldValue.class,
LocalDate.class)
.registerTypeCoercion(
(FieldValue fv) -> LocalTime.parse(fv.getStringValue()),
FieldValue.class,
LocalTime.class)
.registerTypeCoercion(
BigQueryTypeCoercionUtility::parseFieldValueToLocalDateTime,
FieldValue.class,
LocalDateTime.class)
.registerTypeCoercion(
(FieldValue fv) -> parseFieldValueToLocalDateTime(fv).atOffset(ZoneOffset.UTC),
FieldValue.class,
OffsetDateTime.class)
.registerTypeCoercion(
(FieldValue fv) -> parseFieldValueToLocalDateTime(fv).atZone(ZoneOffset.UTC),
FieldValue.class,
ZonedDateTime.class)
.registerTypeCoercion(
(FieldValue fv) -> parseFieldValueToLocalDateTime(fv).toInstant(ZoneOffset.UTC),
FieldValue.class,
Instant.class)
.registerTypeCoercion(new TimestampToString())
.registerTypeCoercion(new TimeToString())
.registerTypeCoercion((Long l) -> l != 0L, Long.class, Boolean.class)
Expand Down Expand Up @@ -276,7 +349,9 @@ private static class TimeToString implements BigQueryCoercion<Time, String> {

@Override
public String coerce(Time value) {
return FORMATTER.format(value.toLocalTime());
LocalTime lt =
Instant.ofEpochMilli(value.getTime()).atZone(ZoneId.systemDefault()).toLocalTime();
return FORMATTER.format(lt);
}
}

Expand Down Expand Up @@ -355,10 +430,11 @@ private static class LongToTimestamp implements BigQueryCoercion<Long, Timestamp

@Override
public Timestamp coerce(Long value) {
// Long value is in microseconds. All further calculations should account for the unit.
Instant instant = Instant.EPOCH.plus(value, ChronoUnit.MICROS);
// Timezone-agnostic conversion preserving exact point in time as mandated by JDBC spec
return Timestamp.from(instant);
LocalDateTime utcDateTime = instant.atOffset(ZoneOffset.UTC).toLocalDateTime();
Timestamp ts = Timestamp.valueOf(utcDateTime);
ts.setNanos((int) ((value % 1_000_000) * 1000));
return ts;
}
Comment on lines 432 to 438

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Using Timestamp.valueOf(utcDateTime) is highly problematic because Timestamp.valueOf(LocalDateTime) interprets the given local date-time in the system default timezone, not in UTC. Since utcDateTime is obtained using ZoneOffset.UTC, converting it via Timestamp.valueOf will shift the actual point in time by the system's timezone offset. For example, if the system timezone is UTC-5, the resulting Timestamp will be shifted by 5 hours. To preserve the exact point in time (as mandated by the JDBC spec), you should use Timestamp.from(instant) or construct the Timestamp directly from the epoch milliseconds while preserving the nanosecond precision.

    @Override
    public Timestamp coerce(Long value) {
      Instant instant = Instant.EPOCH.plus(value, ChronoUnit.MICROS);
      return Timestamp.from(instant);
    }

}

Expand Down Expand Up @@ -390,17 +466,9 @@ private static class FieldValueToTime implements BigQueryCoercion<FieldValue, Ti

@Override
public Time coerce(FieldValue fieldValue) {
// Time ranges from 00:00:00 to 23:59:59.999999 in BigQuery
String strTime = fieldValue.getStringValue();
try {
LocalTime localTime = LocalTime.parse(strTime);
// Convert LocalTime to milliseconds of the day. This correctly preserves millisecond
// precision and truncates anything smaller
long millisOfDay = TimeUnit.NANOSECONDS.toMillis(localTime.toNanoOfDay());
// Adjust by local timezone offset to ensure correct wall-clock representation with
// millisecond precision
long localMillis = TimeZoneCache.getLocalMillis(millisOfDay);
return new Time(localMillis);
return localTimeToTime(LocalTime.parse(strTime));
} catch (java.time.format.DateTimeParseException e) {
IllegalArgumentException ex =
new IllegalArgumentException(
Expand All @@ -416,19 +484,10 @@ private static class FieldValueToTimestamp implements BigQueryCoercion<FieldValu
@Override
public Timestamp coerce(FieldValue fieldValue) {
String rawValue = fieldValue.getStringValue();
// BigQuery DATETIME strings are formatted like "YYYY-MM-DD'T'HH:MM:SS.fffffffff"
// BigQuery TIMESTAMP strings are numeric epoch seconds.
if (rawValue.contains("T")) {
// It's a DATETIME string.
// Timestamp.valueOf() expects "yyyy-mm-dd hh:mm:ss.fffffffff" format.
if (rawValue.contains("T") || rawValue.contains(" ")) {
return Timestamp.valueOf(rawValue.replace('T', ' '));
} else {
// It's a TIMESTAMP numeric string.
long microseconds = fieldValue.getTimestampValue();
Instant instant = Instant.EPOCH.plus(microseconds, ChronoUnit.MICROS);
// Timezone-agnostic conversion preserving exact point in time as mandated by JDBC spec
return Timestamp.from(instant);
}
return new LongToTimestamp().coerce(fieldValue.getTimestampValue());
Comment on lines +487 to +490

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This change is highly error-prone and will break parsing for BigQuery TIMESTAMP values. BigQuery TIMESTAMP string representations typically contain a space (e.g., 2023-07-28 12:30:00 UTC). Under this new logic, because the string contains a space, it will enter the if block and call Timestamp.valueOf(rawValue). However: 1. Timestamp.valueOf does not support timezone suffixes like UTC and will throw an IllegalArgumentException. 2. Even if it did parse, Timestamp.valueOf interprets the time in the system default timezone, whereas BigQuery timestamps are in UTC, leading to incorrect timezone shifts. Please revert this to the previous logic where DATETIME (containing T) is handled separately from TIMESTAMP (which should be retrieved via fieldValue.getTimestampValue() and converted timezone-agnostically).

Suggested change
if (rawValue.contains("T") || rawValue.contains(" ")) {
return Timestamp.valueOf(rawValue.replace('T', ' '));
} else {
// It's a TIMESTAMP numeric string.
long microseconds = fieldValue.getTimestampValue();
Instant instant = Instant.EPOCH.plus(microseconds, ChronoUnit.MICROS);
// Timezone-agnostic conversion preserving exact point in time as mandated by JDBC spec
return Timestamp.from(instant);
}
return new LongToTimestamp().coerce(fieldValue.getTimestampValue());
if (rawValue.contains("T")) {
return Timestamp.valueOf(rawValue.replace('T', ' '));
}
return new LongToTimestamp().coerce(fieldValue.getTimestampValue());

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.Properties;
Expand Down Expand Up @@ -2717,6 +2719,63 @@ public void validateGetTimestamp() throws Exception {
validate("getTimestamp", getter, result);
}

@Test
public void validateGetLocalDate() throws Exception {
final ImmutableMap<String, Object> result =
new ImmutableMap.Builder<String, Object>()
.put("dateField", LocalDate.of(2023, 7, 28))
.put("dateTimeField", LocalDate.of(2023, 7, 28))
.put("timestampFiled", LocalDate.of(2023, 7, 28))
.build();
BiFunction<ResultSet, Integer, Object> getter =
(s, i) -> {
try {
return s.getObject(i, LocalDate.class);
} catch (Exception e) {
return EXCEPTION_REPLACEMENT;
}
};
validate("getLocalDate", getter, result);
}

@Test
public void validateGetLocalTime() throws Exception {
final ImmutableMap<String, Object> result =
new ImmutableMap.Builder<String, Object>()
.put("timeField", LocalTime.of(12, 30, 0))
.put("dateTimeField", LocalTime.of(12, 30, 0))
.put("timestampFiled", LocalTime.of(12, 30, 0))
.build();
BiFunction<ResultSet, Integer, Object> getter =
(s, i) -> {
try {
return s.getObject(i, LocalTime.class);
} catch (Exception e) {
return EXCEPTION_REPLACEMENT;
}
};
validate("getLocalTime", getter, result);
}

@Test
public void validateGetLocalDateTime() throws Exception {
final ImmutableMap<String, Object> result =
new ImmutableMap.Builder<String, Object>()
.put("dateField", LocalDate.of(2023, 7, 28).atStartOfDay())
.put("dateTimeField", LocalDateTime.of(2023, 7, 28, 12, 30, 0))
.put("timestampFiled", LocalDateTime.of(2023, 7, 28, 12, 30, 0))
.build();
BiFunction<ResultSet, Integer, Object> getter =
(s, i) -> {
try {
return s.getObject(i, LocalDateTime.class);
} catch (Exception e) {
return EXCEPTION_REPLACEMENT;
}
};
validate("getLocalDateTime", getter, result);
}

@Test
public void validateGetByte() throws Exception {
final ImmutableMap<String, Object> result =
Expand Down
Loading