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 RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
1. Proxy: Fix column length for PostgreSQL string binary protocol value - [35840](https://github.com/apache/shardingsphere/pull/35840)
1. Proxy: Fix the connection leak caused by rollback failure in Proxy - [35867](https://github.com/apache/shardingsphere/pull/35867)
1. Proxy: Fix the behavior difference of select built-in function names with spaces -[#36537](https://github.com/apache/shardingsphere/pull/36537)
1. Proxy: Fix MySQL text protocol datetime fractional seconds output - [#37410](https://github.com/apache/shardingsphere/pull/37410)
1. Proxy: Fix IndexOutOfBoundsException for MySQL no-FROM multi-projection SELECT routed to admin path - [#37391](https://github.com/apache/shardingsphere/pull/37391)
1. Proxy: Fix MySQL binary protocol datetime/time fractional seconds precision - [#37294](https://github.com/apache/shardingsphere/pull/37294)
1. Proxy: Fix PostgreSQL boolean text output to return `t`/`f` as per protocol - [#37184](https://github.com/apache/shardingsphere/pull/37184)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,27 @@ private void writeDataIntoPayload(final MySQLPacketPayload payload, final Object
} else if (data instanceof Boolean) {
payload.writeBytesLenenc((boolean) data ? new byte[]{1} : new byte[]{0});
} else if (data instanceof LocalDateTime) {
payload.writeStringLenenc(DateTimeFormatterFactory.getDatetimeFormatter().format((LocalDateTime) data));
payload.writeStringLenenc(formatLocalDateTime((LocalDateTime) data));
} else {
payload.writeStringLenenc(data.toString());
}
}

private String formatLocalDateTime(final LocalDateTime value) {
int nanos = value.getNano();
if (0 == nanos) {
return DateTimeFormatterFactory.getDatetimeFormatter().format(value);
}
StringBuilder result = new StringBuilder(DateTimeFormatterFactory.getDatetimeFormatter().format(value)).append('.');
String microsecondsText = String.format("%06d", nanos / 1000);
int endIndex = microsecondsText.length();
while (endIndex > 0 && '0' == microsecondsText.charAt(endIndex - 1)) {
endIndex--;
}
if (0 == endIndex) {
return result.substring(0, result.length() - 1);
}
result.append(microsecondsText, 0, endIndex);
return result.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,12 @@ void assertLocalDateTime() {
actual.write(payload);
verify(payload).writeStringLenenc(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.parse(localDateTimeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))));
}

@Test
void assertLocalDateTimeWithMicros() {
LocalDateTime dateTime = LocalDateTime.of(2022, 2, 18, 17, 32, 38, 123456000);
MySQLTextResultSetRowPacket actual = new MySQLTextResultSetRowPacket(Collections.singletonList(dateTime));
actual.write(payload);
verify(payload).writeStringLenenc("2022-02-18 17:32:38.123456");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,93 +28,212 @@
import com.google.protobuf.StringValue;
import org.junit.jupiter.api.Test;

import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialClob;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Date;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isA;
import static org.junit.jupiter.api.Assertions.assertTrue;

@SuppressWarnings("UseOfObsoleteDateTimeApi")
class ColumnValueConvertUtilsTest {

@SuppressWarnings("UseOfObsoleteDateTimeApi")
@Test
void assertConvertToProtobufMessage() {
Message actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(null);
assertThat(actualMessage, isA(Empty.class));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(1);
assertThat(actualMessage, isA(Int32Value.class));
assertThat(((Int32Value) actualMessage).getValue(), is(1));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage((byte) 1);
assertThat(actualMessage, isA(Int32Value.class));
assertThat(((Int32Value) actualMessage).getValue(), is(1));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage((short) 1);
assertThat(actualMessage, isA(Int32Value.class));
assertThat(((Int32Value) actualMessage).getValue(), is(1));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(1L);
assertThat(actualMessage, isA(Int64Value.class));
assertThat(((Int64Value) actualMessage).getValue(), is(1L));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(new BigInteger("1234"));
assertThat(actualMessage, isA(StringValue.class));
assertThat(new BigInteger(((StringValue) actualMessage).getValue()), is(new BigInteger("1234")));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(1.0F);
assertThat(actualMessage, isA(FloatValue.class));
assertThat(((FloatValue) actualMessage).getValue(), is(1.0F));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(1.23);
assertThat(actualMessage, isA(DoubleValue.class));
assertThat(((DoubleValue) actualMessage).getValue(), is(1.23));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(new BigDecimal("100"));
assertThat(actualMessage, isA(StringValue.class));
assertThat(((StringValue) actualMessage).getValue(), is("100"));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage("abcd");
assertThat(actualMessage, isA(StringValue.class));
assertThat(((StringValue) actualMessage).getValue(), is("abcd"));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(true);
assertThat(actualMessage, isA(BoolValue.class));
assertTrue(((BoolValue) actualMessage).getValue());
Timestamp now = new Timestamp(System.currentTimeMillis());
long epochSecond = now.toInstant().getEpochSecond();
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(now.toLocalDateTime());
assertThat(actualMessage, isA(com.google.protobuf.Timestamp.class));
assertThat(((com.google.protobuf.Timestamp) actualMessage).getSeconds(), is(epochSecond));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(now);
assertThat(actualMessage, isA(com.google.protobuf.Timestamp.class));
assertThat(((com.google.protobuf.Timestamp) actualMessage).getSeconds(), is(epochSecond));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(new Date(now.getTime()));
assertThat(actualMessage, isA(com.google.protobuf.Timestamp.class));
assertThat(((com.google.protobuf.Timestamp) actualMessage).getSeconds(), is(epochSecond));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(now.toInstant());
assertThat(actualMessage, isA(com.google.protobuf.Timestamp.class));
assertThat(((com.google.protobuf.Timestamp) actualMessage).getNanos(), is(now.toInstant().getNano()));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(now.toLocalDateTime().toLocalTime());
assertThat(actualMessage, isA(Int64Value.class));
assertThat(((Int64Value) actualMessage).getValue(), is(now.toLocalDateTime().toLocalTime().toNanoOfDay()));
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage("123456".getBytes());
assertThat(actualMessage, isA(BytesValue.class));
assertThat(((BytesValue) actualMessage).getValue().toByteArray(), is("123456".getBytes()));
OffsetTime offsetTime = OffsetTime.now();
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(offsetTime);
assertThat(actualMessage, isA(Int64Value.class));
assertThat(((Int64Value) actualMessage).getValue(), is(offsetTime.toLocalTime().toNanoOfDay()));
OffsetDateTime offsetDateTime = OffsetDateTime.now();
actualMessage = ColumnValueConvertUtils.convertToProtobufMessage(offsetDateTime);
assertThat(actualMessage, isA(com.google.protobuf.Timestamp.class));
assertThat(((com.google.protobuf.Timestamp) actualMessage).getSeconds(), is(offsetDateTime.toEpochSecond()));
assertThat(((com.google.protobuf.Timestamp) actualMessage).getNanos(), is(offsetDateTime.getNano()));
}

@Test
void assertTimeConvert() {
Time time = new Time(-3600L * 1000L - 1234L);
@Test
void assertConvertNullToEmpty() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(null);
assertThat(actual, isA(Empty.class));
}

@Test
void assertConvertIntegerToInt32Value() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(1);
assertThat(((Int32Value) actual).getValue(), is(1));
}

@Test
void assertConvertShortToInt32Value() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage((short) 2);
assertThat(((Int32Value) actual).getValue(), is(2));
}

@Test
void assertConvertByteToInt32Value() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage((byte) 3);
assertThat(((Int32Value) actual).getValue(), is(3));
}

@Test
void assertConvertLongToInt64Value() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(4L);
assertThat(((Int64Value) actual).getValue(), is(4L));
}

@Test
void assertConvertBigIntegerToStringValue() {
BigInteger expectedBigInteger = new BigInteger("1234");
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(expectedBigInteger);
assertThat(new BigInteger(((StringValue) actual).getValue()), is(expectedBigInteger));
}

@Test
void assertConvertFloatToFloatValue() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(1.5F);
assertThat(((FloatValue) actual).getValue(), is(1.5F));
}

@Test
void assertConvertDoubleToDoubleValue() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(2.5D);
assertThat(((DoubleValue) actual).getValue(), is(2.5D));
}

@Test
void assertConvertBigDecimalToStringValue() {
BigDecimal expectedBigDecimal = new BigDecimal("1000.01");
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(expectedBigDecimal);
assertThat(((StringValue) actual).getValue(), is(expectedBigDecimal.toString()));
}

@Test
void assertConvertStringToStringValue() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage("abcd");
assertThat(((StringValue) actual).getValue(), is("abcd"));
}

@Test
void assertConvertBooleanToBoolValue() {
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(true);
assertTrue(((BoolValue) actual).getValue());
}

@Test
void assertConvertBytesToBytesValue() {
byte[] expectedBytes = "123456".getBytes(StandardCharsets.UTF_8);
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(expectedBytes);
assertThat(((BytesValue) actual).getValue().toByteArray(), is(expectedBytes));
}

@Test
void assertConvertTimeToInt64Value() {
Time time = Time.valueOf(LocalTime.of(1, 2, 3));
int nanos = new Timestamp(time.getTime()).getNanos();
Int64Value actualMessage = (Int64Value) ColumnValueConvertUtils.convertToProtobufMessage(time);
assertThat(LocalTime.ofNanoOfDay(actualMessage.getValue()), is(time.toLocalTime().withNano(nanos)));
Int64Value actual = (Int64Value) ColumnValueConvertUtils.convertToProtobufMessage(time);
assertThat(LocalTime.ofNanoOfDay(actual.getValue()), is(LocalTime.of(1, 2, 3, nanos)));
}

@Test
void assertConvertSqlDateToInt64Value() {
java.sql.Date sqlDate = java.sql.Date.valueOf(LocalDate.of(2020, 1, 2));
Int64Value actual = (Int64Value) ColumnValueConvertUtils.convertToProtobufMessage(sqlDate);
assertThat(actual.getValue(), is(sqlDate.toLocalDate().toEpochDay()));
}

@Test
void assertConvertUtilDateToTimestampMessage() {
Date utilDate = new Date(1_600_000_000_123L);
com.google.protobuf.Timestamp actual = (com.google.protobuf.Timestamp) ColumnValueConvertUtils.convertToProtobufMessage(utilDate);
assertThat(actual.getSeconds(), is(utilDate.getTime() / 1000L));
assertThat(actual.getNanos(), is((int) ((utilDate.getTime() % 1000L) * 1_000_000L)));
}

@Test
void assertConvertLocalDateTimeToTimestampMessage() {
LocalDateTime localDateTime = LocalDateTime.of(2021, 5, 6, 7, 8, 9, 123000000);
com.google.protobuf.Timestamp actual = (com.google.protobuf.Timestamp) ColumnValueConvertUtils.convertToProtobufMessage(localDateTime);
Timestamp expectedTimestamp = Timestamp.valueOf(localDateTime);
assertThat(actual.getSeconds(), is(expectedTimestamp.getTime() / 1000L));
assertThat(actual.getNanos(), is(expectedTimestamp.getNanos()));
}

@Test
void assertConvertLocalDateToInt64Value() {
LocalDate localDate = LocalDate.of(2022, 3, 4);
Int64Value actual = (Int64Value) ColumnValueConvertUtils.convertToProtobufMessage(localDate);
assertThat(actual.getValue(), is(localDate.toEpochDay()));
}

@Test
void assertConvertLocalTimeToInt64Value() {
LocalTime localTime = LocalTime.of(5, 6, 7, 8);
Int64Value actual = (Int64Value) ColumnValueConvertUtils.convertToProtobufMessage(localTime);
assertThat(actual.getValue(), is(localTime.toNanoOfDay()));
}

@Test
void assertConvertOffsetDateTimeToTimestampMessage() {
OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(2020, 1, 2, 3, 4, 5, 600000000), ZoneOffset.ofHours(1));
com.google.protobuf.Timestamp actual = (com.google.protobuf.Timestamp) ColumnValueConvertUtils.convertToProtobufMessage(offsetDateTime);
Timestamp expectedTimestamp = Timestamp.valueOf(offsetDateTime.toLocalDateTime());
assertThat(actual.getSeconds(), is(expectedTimestamp.getTime() / 1000L));
assertThat(actual.getNanos(), is(expectedTimestamp.getNanos()));
}

@Test
void assertConvertOffsetTimeToInt64Value() {
OffsetTime offsetTime = OffsetTime.of(1, 2, 3, 4, ZoneOffset.ofHours(-2));
Int64Value actual = (Int64Value) ColumnValueConvertUtils.convertToProtobufMessage(offsetTime);
assertThat(actual.getValue(), is(offsetTime.toLocalTime().toNanoOfDay()));
}

@Test
void assertConvertZonedDateTimeToTimestampMessage() {
ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 7, 8, 9, 10, 11, 120000000), ZoneId.of("UTC"));
com.google.protobuf.Timestamp actual = (com.google.protobuf.Timestamp) ColumnValueConvertUtils.convertToProtobufMessage(zonedDateTime);
Timestamp expectedTimestamp = Timestamp.valueOf(zonedDateTime.toLocalDateTime());
assertThat(actual.getSeconds(), is(expectedTimestamp.getTime() / 1000L));
assertThat(actual.getNanos(), is(expectedTimestamp.getNanos()));
}

@Test
void assertConvertInstantToTimestampMessage() {
Instant instant = Instant.ofEpochSecond(1_700_000_000L, 123_000_000L);
com.google.protobuf.Timestamp actual = (com.google.protobuf.Timestamp) ColumnValueConvertUtils.convertToProtobufMessage(instant);
assertThat(actual.getSeconds(), is(instant.getEpochSecond()));
assertThat(actual.getNanos(), is(instant.getNano()));
}

@Test
void assertConvertClobToStringValue() throws SQLException {
SerialClob clob = new SerialClob("clob_value".toCharArray());
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(clob);
assertThat(((StringValue) actual).getValue(), is("clob_value"));
}

@Test
void assertConvertBlobToBytesValue() throws SQLException {
byte[] expectedBlobBytes = "blob_value".getBytes(StandardCharsets.UTF_8);
SerialBlob blob = new SerialBlob(expectedBlobBytes);
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(blob);
assertThat(((BytesValue) actual).getValue().toByteArray(), is(expectedBlobBytes));
}

@Test
void assertConvertCustomObjectToStringValue() {
Object customObject = new Object() {

@Override
public String toString() {
return "custom_object";
}
};
Message actual = ColumnValueConvertUtils.convertToProtobufMessage(customObject);
assertThat(((StringValue) actual).getValue(), is("custom_object"));
}
}