This is an automated email from the ASF dual-hosted git repository.
terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new f6b2576ed5f jdbc: add MySQL-compatible typed string conversion (#38444)
f6b2576ed5f is described below
commit f6b2576ed5fd03aa9caa8d23715c737eed34a5ec
Author: Aviral Garg <[email protected]>
AuthorDate: Sat Jun 6 11:34:54 2026 +0530
jdbc: add MySQL-compatible typed string conversion (#38444)
* jdbc: add MySQL-compatible typed string conversion
* jdbc: use executed database protocol in result set conversion
* infra,jdbc: drive typed conversion by protocol type
* infra,jdbc: refine typed conversion assertions
* infra: align MySQL typed string conversion semantics
* infra: fix spotless for ResultSetUtilsTest
* ci: disable Ryuk in e2e sql workflow
* jdbc,infra: isolate MySQL typed result set conversion
---
.../impl/driver/jdbc/type/util/ResultSetUtils.java | 6 +-
.../driver/jdbc/type/util/ResultSetUtilsTest.java | 5 +
.../resultset/DialectResultSetValueConverter.java | 40 +++++
.../resultset/MySQLResultSetValueConverter.java | 146 +++++++++++++++++
.../core/resultset/ShardingSphereResultSet.java | 33 +++-
.../statement/ShardingSpherePreparedStatement.java | 1 +
.../core/statement/ShardingSphereStatement.java | 1 +
...c.core.resultset.DialectResultSetValueConverter | 18 +++
.../MySQLResultSetValueConverterTest.java | 177 +++++++++++++++++++++
.../resultset/ShardingSphereResultSetTest.java | 97 ++++++++++-
10 files changed, 514 insertions(+), 10 deletions(-)
diff --git
a/infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/driver/jdbc/type/util/ResultSetUtils.java
b/infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/driver/jdbc/type/util/ResultSetUtils.java
index 68ecc25cbec..f88c907bf26 100644
---
a/infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/driver/jdbc/type/util/ResultSetUtils.java
+++
b/infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/driver/jdbc/type/util/ResultSetUtils.java
@@ -94,9 +94,6 @@ public final class ResultSetUtils {
if (value instanceof byte[]) {
return convertByteArrayValue((byte[]) value, convertType);
}
- if (boolean.class.equals(convertType)) {
- return convertBooleanValue(value);
- }
if (String.class.equals(convertType)) {
return value.toString();
}
@@ -106,6 +103,9 @@ public final class ResultSetUtils {
return result.get();
}
}
+ if (boolean.class.equals(convertType)) {
+ return convertBooleanValue(value);
+ }
try {
return convertType.cast(value);
} catch (final ClassCastException ignored) {
diff --git
a/infra/executor/src/test/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/driver/jdbc/type/util/ResultSetUtilsTest.java
b/infra/executor/src/test/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/driver/jdbc/type/util/ResultSetUtilsTest.java
index 6a7aa3c1f1a..45074fdd54d 100644
---
a/infra/executor/src/test/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/driver/jdbc/type/util/ResultSetUtilsTest.java
+++
b/infra/executor/src/test/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/driver/jdbc/type/util/ResultSetUtilsTest.java
@@ -125,6 +125,11 @@ class ResultSetUtilsTest {
assertThrows(UnsupportedDataTypeConversionException.class, () ->
ResultSetUtils.convertValue(1, Date.class));
}
+ @Test
+ void assertConvertStringToInteger() {
+ assertThrows(SQLFeatureNotSupportedException.class, () ->
ResultSetUtils.convertValue("123", Integer.class));
+ }
+
@Test
void assertConvertDateValue() throws SQLException {
Date now = new Date();
diff --git
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/DialectResultSetValueConverter.java
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/DialectResultSetValueConverter.java
new file mode 100644
index 00000000000..d9e8f77b190
--- /dev/null
+++
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/DialectResultSetValueConverter.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.driver.jdbc.core.resultset;
+
+import org.apache.shardingsphere.infra.spi.annotation.SingletonSPI;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPI;
+
+import java.sql.SQLFeatureNotSupportedException;
+
+/**
+ * Dialect result set value converter.
+ */
+@SingletonSPI
+public interface DialectResultSetValueConverter extends TypedSPI {
+
+ /**
+ * Convert value via expected class type.
+ *
+ * @param value original value
+ * @param convertType expected class type
+ * @return converted value
+ * @throws SQLFeatureNotSupportedException SQL feature not supported
exception
+ */
+ Object convertValue(Object value, Class<?> convertType) throws
SQLFeatureNotSupportedException;
+}
diff --git
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/MySQLResultSetValueConverter.java
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/MySQLResultSetValueConverter.java
new file mode 100644
index 00000000000..2308ff7f0b2
--- /dev/null
+++
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/MySQLResultSetValueConverter.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.driver.jdbc.core.resultset;
+
+import
org.apache.shardingsphere.infra.exception.kernel.data.UnsupportedDataTypeConversionException;
+import
org.apache.shardingsphere.infra.executor.sql.execute.result.query.impl.driver.jdbc.type.util.ResultSetUtils;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.nio.charset.StandardCharsets;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.regex.Pattern;
+
+/**
+ * MySQL result set value converter.
+ */
+public final class MySQLResultSetValueConverter implements
DialectResultSetValueConverter {
+
+ private static final Pattern FLOATING_POINT_PATTERN =
Pattern.compile("-?\\d*\\.\\d*");
+
+ private static final Pattern INTEGER_PATTERN = Pattern.compile("-?\\d+");
+
+ @Override
+ public Object convertValue(final Object value, final Class<?> convertType)
throws SQLFeatureNotSupportedException {
+ if (!(value instanceof String)) {
+ return ResultSetUtils.convertValue(value, convertType);
+ }
+ String stringValue = (String) value;
+ try {
+ switch (convertType.getName()) {
+ case "boolean":
+ case "java.lang.Boolean":
+ return stringValue.isEmpty() ? false :
convertBooleanValue(stringValue, convertType);
+ case "byte":
+ case "java.lang.Byte":
+ return stringValue.isEmpty() ? (byte) 0 :
convertByteValue(stringValue, convertType);
+ case "short":
+ case "java.lang.Short":
+ return stringValue.isEmpty() ? (short) 0 :
convertIntegralValue(stringValue, convertType).shortValueExact();
+ case "int":
+ case "java.lang.Integer":
+ return stringValue.isEmpty() ? 0 :
convertIntegralValue(stringValue, convertType).intValueExact();
+ case "long":
+ case "java.lang.Long":
+ return stringValue.isEmpty() ? 0L :
convertIntegralValue(stringValue, convertType).longValueExact();
+ case "double":
+ case "java.lang.Double":
+ return stringValue.isEmpty() ? 0.0D :
convertNumericValue(stringValue, convertType).doubleValue();
+ case "float":
+ case "java.lang.Float":
+ return stringValue.isEmpty() ? 0.0F :
convertNumericValue(stringValue, convertType).floatValue();
+ case "java.math.BigDecimal":
+ return stringValue.isEmpty() ? BigDecimal.ZERO :
convertNumericValue(stringValue, convertType);
+ default:
+ return ResultSetUtils.convertValue(value, convertType);
+ }
+ } catch (final NumberFormatException | ArithmeticException ignored) {
+ throw new UnsupportedDataTypeConversionException(convertType,
value);
+ }
+ }
+
+ @Override
+ public String getType() {
+ return "MySQL";
+ }
+
+ private byte convertByteValue(final String value, final Class<?>
convertType) {
+ byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
+ if (1 != bytes.length) {
+ throw new UnsupportedDataTypeConversionException(convertType,
value);
+ }
+ return bytes[0];
+ }
+
+ private Boolean convertBooleanValue(final String value, final Class<?>
convertType) {
+ if ("Y".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value) ||
"T".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value)) {
+ return true;
+ }
+ if ("N".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value) ||
"F".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
+ return false;
+ }
+ if (value.contains("e") || value.contains("E") ||
FLOATING_POINT_PATTERN.matcher(value).matches()) {
+ return convertDoubleToBoolean(Double.parseDouble(value));
+ }
+ if (INTEGER_PATTERN.matcher(value).matches()) {
+ return isSignedLong(value) ? longToBoolean(Long.parseLong(value))
: convertBigIntegerToBoolean(new BigInteger(value));
+ }
+ throw new UnsupportedDataTypeConversionException(convertType, value);
+ }
+
+ private BigDecimal convertIntegralValue(final String value, final Class<?>
convertType) {
+ if (value.contains("e") || value.contains("E") ||
FLOATING_POINT_PATTERN.matcher(value).matches()) {
+ return BigDecimal.valueOf(Double.parseDouble(value)).setScale(0,
RoundingMode.DOWN);
+ }
+ if (INTEGER_PATTERN.matcher(value).matches()) {
+ return convertIntegerValue(value);
+ }
+ throw new UnsupportedDataTypeConversionException(convertType, value);
+ }
+
+ private BigDecimal convertNumericValue(final String value, final Class<?>
convertType) {
+ if (value.contains("e") || value.contains("E") ||
FLOATING_POINT_PATTERN.matcher(value).matches()) {
+ return BigDecimal.valueOf(Double.parseDouble(value));
+ }
+ if (INTEGER_PATTERN.matcher(value).matches()) {
+ return convertIntegerValue(value);
+ }
+ throw new UnsupportedDataTypeConversionException(convertType, value);
+ }
+
+ private BigDecimal convertIntegerValue(final String value) {
+ return isSignedLong(value) ? BigDecimal.valueOf(Long.parseLong(value))
: new BigDecimal(new BigInteger(value));
+ }
+
+ private boolean isSignedLong(final String value) {
+ return '-' == value.charAt(0) || value.length() <= 18 &&
value.charAt(0) >= '0' && value.charAt(0) <= '8';
+ }
+
+ private boolean longToBoolean(final long value) {
+ return -1L == value || value > 0L;
+ }
+
+ private boolean convertDoubleToBoolean(final double value) {
+ return value > 0.0D || -1.0D == value;
+ }
+
+ private boolean convertBigIntegerToBoolean(final BigInteger value) {
+ return value.signum() > 0 || BigInteger.valueOf(-1L).equals(value);
+ }
+}
diff --git
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/ShardingSphereResultSet.java
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/ShardingSphereResultSet.java
index 6081e1c7f1b..d1b97386189 100644
---
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/ShardingSphereResultSet.java
+++
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/resultset/ShardingSphereResultSet.java
@@ -18,11 +18,15 @@
package org.apache.shardingsphere.driver.jdbc.core.resultset;
import lombok.Getter;
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.driver.jdbc.adapter.AbstractResultSetAdapter;
+import
org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement;
+import
org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSphereStatement;
import org.apache.shardingsphere.infra.annotation.HighFrequencyInvocation;
import
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
import
org.apache.shardingsphere.infra.executor.sql.execute.result.query.impl.driver.jdbc.type.util.ResultSetUtils;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
import org.apache.shardingsphere.infra.merge.result.MergedResult;
import java.io.InputStream;
@@ -63,12 +67,16 @@ public final class ShardingSphereResultSet extends
AbstractResultSetAdapter {
private final MergedResult mergeResultSet;
+ private final DialectResultSetValueConverter
dialectResultSetValueConverter;
+
@Getter
private final Map<String, Integer> columnLabelAndIndexMap;
public ShardingSphereResultSet(final List<ResultSet> resultSets, final
MergedResult mergeResultSet, final Statement statement, final
SQLStatementContext sqlStatementContext) throws SQLException {
super(resultSets, statement, sqlStatementContext);
this.mergeResultSet = mergeResultSet;
+ DatabaseType protocolType = getProtocolType(statement);
+ dialectResultSetValueConverter =
getDialectResultSetValueConverter(protocolType);
columnLabelAndIndexMap =
ShardingSphereResultSetUtils.createColumnLabelAndIndexMap(sqlStatementContext,
resultSets.get(0).getMetaData());
}
@@ -76,6 +84,8 @@ public final class ShardingSphereResultSet extends
AbstractResultSetAdapter {
final SQLStatementContext
sqlStatementContext, final Map<String, Integer> columnLabelAndIndexMap) {
super(resultSets, statement, sqlStatementContext);
this.mergeResultSet = mergeResultSet;
+ DatabaseType protocolType = getProtocolType(statement);
+ dialectResultSetValueConverter =
getDialectResultSetValueConverter(protocolType);
this.columnLabelAndIndexMap = columnLabelAndIndexMap;
}
@@ -383,9 +393,9 @@ public final class ShardingSphereResultSet extends
AbstractResultSetAdapter {
return (T) getClob(columnIndex);
}
if (LocalDateTime.class.equals(type) || LocalDate.class.equals(type)
|| LocalTime.class.equals(type) || OffsetDateTime.class.equals(type)) {
- return (T)
ResultSetUtils.convertValue(mergeResultSet.getValue(columnIndex,
Timestamp.class), type);
+ return (T) convertValue(mergeResultSet.getValue(columnIndex,
Timestamp.class), type);
}
- return (T)
ResultSetUtils.convertValue(mergeResultSet.getValue(columnIndex, type), type);
+ return (T) convertValue(mergeResultSet.getValue(columnIndex, type),
type);
}
@Override
@@ -398,4 +408,23 @@ public final class ShardingSphereResultSet extends
AbstractResultSetAdapter {
ShardingSpherePreconditions.checkNotNull(result, () -> new
SQLFeatureNotSupportedException(String.format("Can not get index from column
label `%s`.", columnLabel)));
return result;
}
+
+ private DatabaseType getProtocolType(final Statement statement) {
+ if (statement instanceof ShardingSpherePreparedStatement) {
+ return ((ShardingSpherePreparedStatement)
statement).getUsedDatabase().getProtocolType();
+ }
+ if (statement instanceof ShardingSphereStatement) {
+ ShardingSphereStatement shardingSphereStatement =
(ShardingSphereStatement) statement;
+ return
shardingSphereStatement.getConnection().getContextManager().getMetaDataContexts().getMetaData().getDatabase(shardingSphereStatement.getUsedDatabaseName()).getProtocolType();
+ }
+ return null;
+ }
+
+ private DialectResultSetValueConverter
getDialectResultSetValueConverter(final DatabaseType protocolType) {
+ return null == protocolType ? null :
TypedSPILoader.findService(DialectResultSetValueConverter.class,
protocolType.getType()).orElse(null);
+ }
+
+ private Object convertValue(final Object value, final Class<?> type)
throws SQLFeatureNotSupportedException {
+ return null == dialectResultSetValueConverter ?
ResultSetUtils.convertValue(value, type) :
dialectResultSetValueConverter.convertValue(value, type);
+ }
}
diff --git
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSpherePreparedStatement.java
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSpherePreparedStatement.java
index 8e75d34e209..7c291d6f528 100644
---
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSpherePreparedStatement.java
+++
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSpherePreparedStatement.java
@@ -81,6 +81,7 @@ public final class ShardingSpherePreparedStatement extends
AbstractPreparedState
private final SQLStatementContext sqlStatementContext;
+ @Getter
private final ShardingSphereDatabase usedDatabase;
private final StatementOption statementOption;
diff --git
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatement.java
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatement.java
index 31d951e1227..af377f29cad 100644
---
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatement.java
+++
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatement.java
@@ -80,6 +80,7 @@ public final class ShardingSphereStatement extends
AbstractStatementAdapter {
private final List<Statement> statements;
+ @Getter
private String usedDatabaseName;
private QueryContext queryContext;
diff --git
a/jdbc/src/main/resources/META-INF/services/org.apache.shardingsphere.driver.jdbc.core.resultset.DialectResultSetValueConverter
b/jdbc/src/main/resources/META-INF/services/org.apache.shardingsphere.driver.jdbc.core.resultset.DialectResultSetValueConverter
new file mode 100644
index 00000000000..b8e5f5761ab
--- /dev/null
+++
b/jdbc/src/main/resources/META-INF/services/org.apache.shardingsphere.driver.jdbc.core.resultset.DialectResultSetValueConverter
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.shardingsphere.driver.jdbc.core.resultset.MySQLResultSetValueConverter
diff --git
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/resultset/MySQLResultSetValueConverterTest.java
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/resultset/MySQLResultSetValueConverterTest.java
new file mode 100644
index 00000000000..5127e9eb400
--- /dev/null
+++
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/resultset/MySQLResultSetValueConverterTest.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.driver.jdbc.core.resultset;
+
+import
org.apache.shardingsphere.infra.exception.kernel.data.UnsupportedDataTypeConversionException;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class MySQLResultSetValueConverterTest {
+
+ private final DialectResultSetValueConverter actual =
TypedSPILoader.getService(DialectResultSetValueConverter.class, "MySQL");
+
+ @Test
+ void assertGetType() {
+ assertThat(actual.getType(), is("MySQL"));
+ }
+
+ @Test
+ void assertConvertStringToBoolean() throws SQLException {
+ assertTrue((boolean) actual.convertValue("true", Boolean.class));
+ }
+
+ @Test
+ void assertConvertSingleCharacterStringToByte() throws SQLException {
+ assertThat(actual.convertValue("1", Byte.class), is((Object)
Byte.valueOf((byte) '1')));
+ }
+
+ @Test
+ void assertConvertMultiCharacterStringToByte() {
+ assertThrows(UnsupportedDataTypeConversionException.class, () ->
actual.convertValue("127", Byte.class));
+ }
+
+ @Test
+ void assertConvertSingleMultiByteCharacterStringToByte() {
+ assertThrows(UnsupportedDataTypeConversionException.class, () ->
actual.convertValue("你", Byte.class));
+ }
+
+ @Test
+ void assertConvertStringToShort() throws SQLException {
+ assertThat(actual.convertValue("32767", Short.class), is((Object)
Short.valueOf((short) 32767)));
+ }
+
+ @Test
+ void assertConvertStringToInteger() throws SQLException {
+ assertThat(actual.convertValue("123", Integer.class), is((Object)
Integer.valueOf(123)));
+ }
+
+ @Test
+ void assertConvertStringWithWhitespacesToInteger() {
+ assertThrows(UnsupportedDataTypeConversionException.class, () ->
actual.convertValue(" 123 ", Integer.class));
+ }
+
+ @Test
+ void assertConvertLeadingPlusStringToInteger() {
+ assertThrows(UnsupportedDataTypeConversionException.class, () ->
actual.convertValue("+123", Integer.class));
+ }
+
+ @Test
+ void assertConvertStringToLong() throws SQLException {
+ assertThat(actual.convertValue("123456", Long.class), is((Object)
Long.valueOf(123456L)));
+ }
+
+ @Test
+ void assertConvertStringToFloat() throws SQLException {
+ assertThat(actual.convertValue("3.14", Float.class), is((Object)
Float.valueOf(3.14F)));
+ }
+
+ @Test
+ void assertConvertStringToDouble() throws SQLException {
+ assertThat(actual.convertValue("3.1415926", Double.class), is((Object)
Double.valueOf(3.1415926D)));
+ }
+
+ @Test
+ void assertConvertStringToBigDecimal() throws SQLException {
+ assertThat(actual.convertValue("123.45", BigDecimal.class),
is((Object) new BigDecimal("123.45")));
+ }
+
+ @Test
+ void assertConvertLeadingPlusStringToBigDecimal() {
+ assertThrows(UnsupportedDataTypeConversionException.class, () ->
actual.convertValue("+123.45", BigDecimal.class));
+ }
+
+ @Test
+ void assertConvertInvalidStringToInteger() {
+ assertThrows(UnsupportedDataTypeConversionException.class, () ->
actual.convertValue("abc", Integer.class));
+ }
+
+ @Test
+ void assertConvertNumericStringOneToBoolean() throws SQLException {
+ assertTrue((boolean) actual.convertValue("1", boolean.class));
+ assertTrue((boolean) actual.convertValue("-1", boolean.class));
+ }
+
+ @Test
+ void assertConvertNumericStringZeroToBoolean() throws SQLException {
+ assertFalse((boolean) actual.convertValue("0", boolean.class));
+ }
+
+ @Test
+ void assertConvertNumericStringTwoToBoolean() throws SQLException {
+ assertTrue((boolean) actual.convertValue("2", boolean.class));
+ }
+
+ @Test
+ void assertConvertFloatingStringToBoolean() throws SQLException {
+ assertTrue((boolean) actual.convertValue("0.1", boolean.class));
+ }
+
+ @Test
+ void assertConvertExponentStringToBoolean() throws SQLException {
+ assertTrue((boolean) actual.convertValue("1e3", boolean.class));
+ }
+
+ @Test
+ void assertConvertInvalidStringToBoolean() {
+ assertThrows(UnsupportedDataTypeConversionException.class, () ->
actual.convertValue("unknown", Boolean.class));
+ }
+
+ @Test
+ void assertConvertEmptyStringToInt() throws SQLException {
+ int actualValue = (int) actual.convertValue("", int.class);
+ int expectedValue = 0;
+ assertThat(actualValue, is(expectedValue));
+ }
+
+ @Test
+ void assertConvertEmptyStringToBoolean() throws SQLException {
+ boolean actualValue = (boolean) actual.convertValue("", boolean.class);
+ assertFalse(actualValue);
+ }
+
+ @Test
+ void assertConvertDecimalStringToInt() throws SQLException {
+ int actualValue = (int) actual.convertValue("123.45", int.class);
+ int expectedValue = 123;
+ assertThat(actualValue, is(expectedValue));
+ }
+
+ @Test
+ void assertConvertExponentStringToInt() throws SQLException {
+ int actualValue = (int) actual.convertValue("1e3", int.class);
+ int expectedValue = 1000;
+ assertThat(actualValue, is(expectedValue));
+ }
+
+ @Test
+ void assertConvertStringToTimestamp() throws SQLException {
+ Timestamp actualValue = (Timestamp) actual.convertValue("2021-12-23
19:30:00", Timestamp.class);
+ Timestamp expectedValue = Timestamp.valueOf("2021-12-23 19:30:00");
+ assertThat(actualValue, is(expectedValue));
+ }
+}
diff --git
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/resultset/ShardingSphereResultSetTest.java
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/resultset/ShardingSphereResultSetTest.java
index 0621b4ec432..b7ba2245738 100644
---
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/resultset/ShardingSphereResultSetTest.java
+++
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/resultset/ShardingSphereResultSetTest.java
@@ -17,11 +17,14 @@
package org.apache.shardingsphere.driver.jdbc.core.resultset;
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import
org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
+import
org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement;
import
org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSphereStatement;
import
org.apache.shardingsphere.infra.binder.context.segment.table.TablesContext;
import
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
+import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import org.apache.shardingsphere.infra.merge.result.MergedResult;
import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
import org.junit.jupiter.api.BeforeEach;
@@ -51,12 +54,14 @@ import java.time.OffsetDateTime;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.Properties;
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.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
@@ -68,6 +73,14 @@ class ShardingSphereResultSetTest {
private ShardingSphereResultSet shardingSphereResultSet;
+ private DatabaseType protocolType;
+
+ private ShardingSphereConnection connection;
+
+ private MetaDataContexts metaDataContexts;
+
+ private ShardingSphereStatement statement;
+
@BeforeEach
void setUp() throws SQLException {
mergeResultSet = mock(MergedResult.class);
@@ -92,13 +105,19 @@ class ShardingSphereResultSetTest {
}
private ShardingSphereStatement getShardingSphereStatement() {
- ShardingSphereConnection connection =
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
- MetaDataContexts metaDataContexts = mock(MetaDataContexts.class,
RETURNS_DEEP_STUBS);
+ connection = mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
+ metaDataContexts = mock(MetaDataContexts.class, RETURNS_DEEP_STUBS);
when(metaDataContexts.getMetaData().getProps()).thenReturn(new
ConfigurationProperties(new Properties()));
+ when(connection.getCurrentDatabaseName()).thenReturn("logic_db");
+ protocolType = mock(DatabaseType.class);
+ when(protocolType.getType()).thenReturn("MySQL");
+ when(protocolType.getTrunkDatabaseType()).thenReturn(Optional.empty());
+
when(metaDataContexts.getMetaData().getDatabase("logic_db").getProtocolType()).thenReturn(protocolType);
when(connection.getContextManager().getMetaDataContexts()).thenReturn(metaDataContexts);
- ShardingSphereStatement result = mock(ShardingSphereStatement.class);
- when(result.getConnection()).thenReturn(connection);
- return result;
+ statement = mock(ShardingSphereStatement.class);
+ when(statement.getConnection()).thenReturn(connection);
+ when(statement.getUsedDatabaseName()).thenReturn("logic_db");
+ return statement;
}
@Test
@@ -572,6 +591,74 @@ class ShardingSphereResultSetTest {
assertThat(shardingSphereResultSet.getObject(1, Integer.class),
is(result));
}
+ @Test
+ void assertGetObjectWithIntegerFromStringInMySQLProtocol() throws
SQLException {
+ when(mergeResultSet.getValue(1, Integer.class)).thenReturn("123");
+ assertThat(shardingSphereResultSet.getObject(1, Integer.class),
is(123));
+ }
+
+ @Test
+ void assertGetObjectWithIntegerFromStringInNonMySQLProtocol() throws
SQLException {
+ when(mergeResultSet.getValue(1, Integer.class)).thenReturn("123");
+ DatabaseType actualProtocolType = mock(DatabaseType.class);
+ when(actualProtocolType.getType()).thenReturn("PostgreSQL");
+
when(actualProtocolType.getTrunkDatabaseType()).thenReturn(Optional.empty());
+
when(metaDataContexts.getMetaData().getDatabase("logic_db").getProtocolType()).thenReturn(actualProtocolType);
+ ShardingSphereResultSet actualResultSet = new
ShardingSphereResultSet(getResultSets(), mergeResultSet, statement,
createSQLStatementContext());
+ assertThrows(SQLException.class, () -> actualResultSet.getObject(1,
Integer.class));
+ }
+
+ @Test
+ void assertGetObjectWithIntegerFromStringInTrunkMySQLProtocol() throws
SQLException {
+ when(mergeResultSet.getValue(1, Integer.class)).thenReturn("123");
+ DatabaseType trunkDatabaseType = mock(DatabaseType.class);
+ when(trunkDatabaseType.getType()).thenReturn("MySQL");
+
when(trunkDatabaseType.getTrunkDatabaseType()).thenReturn(Optional.empty());
+ DatabaseType actualProtocolType = mock(DatabaseType.class);
+ when(actualProtocolType.getType()).thenReturn("MariaDB");
+
when(actualProtocolType.getTrunkDatabaseType()).thenReturn(Optional.of(trunkDatabaseType));
+
when(metaDataContexts.getMetaData().getDatabase("logic_db").getProtocolType()).thenReturn(actualProtocolType);
+ ShardingSphereResultSet actualResultSet = new
ShardingSphereResultSet(getResultSets(), mergeResultSet, statement,
createSQLStatementContext());
+ assertThrows(SQLException.class, () -> actualResultSet.getObject(1,
Integer.class));
+ }
+
+ @Test
+ void
assertGetObjectWithIntegerFromStringUsesUsedDatabaseProtocolForStatement()
throws SQLException {
+ ShardingSphereConnection statementConnection =
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
+ MetaDataContexts statementMetaDataContexts =
mock(MetaDataContexts.class, RETURNS_DEEP_STUBS);
+
when(statementMetaDataContexts.getMetaData().getProps()).thenReturn(new
ConfigurationProperties(new Properties()));
+ DatabaseType currentDatabaseProtocolType = mock(DatabaseType.class);
+ when(currentDatabaseProtocolType.getType()).thenReturn("PostgreSQL");
+
when(currentDatabaseProtocolType.getTrunkDatabaseType()).thenReturn(Optional.empty());
+ DatabaseType usedDatabaseProtocolType = mock(DatabaseType.class);
+ when(usedDatabaseProtocolType.getType()).thenReturn("MySQL");
+
when(usedDatabaseProtocolType.getTrunkDatabaseType()).thenReturn(Optional.empty());
+ ShardingSphereStatement usedDatabaseStatement =
mock(ShardingSphereStatement.class);
+
when(statementConnection.getCurrentDatabaseName()).thenReturn("current_db");
+
when(statementConnection.getContextManager().getMetaDataContexts()).thenReturn(statementMetaDataContexts);
+
when(statementMetaDataContexts.getMetaData().getDatabase("current_db").getProtocolType()).thenReturn(currentDatabaseProtocolType);
+
when(statementMetaDataContexts.getMetaData().getDatabase("used_db").getProtocolType()).thenReturn(usedDatabaseProtocolType);
+
when(usedDatabaseStatement.getConnection()).thenReturn(statementConnection);
+
when(usedDatabaseStatement.getUsedDatabaseName()).thenReturn("used_db");
+ ShardingSphereResultSet usedDatabaseResultSet = new
ShardingSphereResultSet(getResultSets(), mergeResultSet, usedDatabaseStatement,
createSQLStatementContext());
+ when(mergeResultSet.getValue(1, Integer.class)).thenReturn("123");
+ assertThat(usedDatabaseResultSet.getObject(1, Integer.class), is(123));
+ }
+
+ @Test
+ void
assertGetObjectWithIntegerFromStringUsesUsedDatabaseProtocolForPreparedStatement()
throws SQLException {
+ ShardingSpherePreparedStatement preparedStatement =
mock(ShardingSpherePreparedStatement.class);
+ ShardingSphereDatabase usedDatabase =
mock(ShardingSphereDatabase.class);
+ DatabaseType usedDatabaseProtocolType = mock(DatabaseType.class);
+ when(usedDatabaseProtocolType.getType()).thenReturn("MySQL");
+
when(usedDatabaseProtocolType.getTrunkDatabaseType()).thenReturn(Optional.empty());
+
when(usedDatabase.getProtocolType()).thenReturn(usedDatabaseProtocolType);
+ when(preparedStatement.getUsedDatabase()).thenReturn(usedDatabase);
+ ShardingSphereResultSet preparedResultSet = new
ShardingSphereResultSet(getResultSets(), mergeResultSet, preparedStatement,
createSQLStatementContext());
+ when(mergeResultSet.getValue(1, Integer.class)).thenReturn("123");
+ assertThat(preparedResultSet.getObject(1, Integer.class), is(123));
+ }
+
@Test
void assertGetObjectWithLong() throws SQLException {
long result = 0L;