diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java index a51dd937..30c8e076 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java @@ -16,6 +16,8 @@ package io.asyncer.r2dbc.mysql.codec; +import java.math.BigInteger; + import io.asyncer.r2dbc.mysql.MySqlParameter; import io.asyncer.r2dbc.mysql.ParameterWriter; import io.asyncer.r2dbc.mysql.api.MySqlReadableMetadata; @@ -38,18 +40,27 @@ private BooleanCodec() { @Override public Boolean decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context) { - MySqlType dataType = metadata.getType(); - if (dataType == MySqlType.VARCHAR) { - if (value.isReadable()) { - String stringVal = value.toString(metadata.getCharCollation(context).getCharset()); - if (stringVal.equalsIgnoreCase("true") || stringVal.equals("1")) { - return true; - } else if (stringVal.equalsIgnoreCase("false") || stringVal.equals("0")) { - return false; - } + if (!value.isReadable()) { + return createFromLong(0); + } + + String s = value.toString(metadata.getCharCollation(context).getCharset()); + + if (s.equalsIgnoreCase("Y") || s.equalsIgnoreCase("yes") || + s.equalsIgnoreCase("T") || s.equalsIgnoreCase("true")) { + return createFromLong(1); + } else if (s.equalsIgnoreCase("N") || s.equalsIgnoreCase("no") || + s.equalsIgnoreCase("F") || s.equalsIgnoreCase("false")) { + return createFromLong(0); + } else if (s.contains("e") || s.contains("E") || s.matches("-?\\d*\\.\\d*")) { + return createFromDouble(Double.parseDouble(s)); + } else if (s.matches("-?\\d+")) { + if (!CodecUtils.isGreaterThanLongMax(s)) { + return createFromLong(CodecUtils.parseLong(value)); } + return createFromBigInteger(new BigInteger(s)); } - return binary || dataType == MySqlType.BIT ? value.readBoolean() : value.readByte() != '0'; + throw new IllegalArgumentException("Unable to interpret string: " + s); } @Override @@ -65,8 +76,19 @@ public MySqlParameter encode(Object value, CodecContext context) { @Override public boolean doCanDecode(MySqlReadableMetadata metadata) { MySqlType type = metadata.getType(); - return ((type == MySqlType.BIT || type == MySqlType.TINYINT) && - Integer.valueOf(1).equals(metadata.getPrecision())) || type == MySqlType.VARCHAR; + return type == MySqlType.BIT || type == MySqlType.VARCHAR || type.isNumeric(); + } + + public Boolean createFromLong(long l) { + return (l == -1 || l > 0); + } + + public Boolean createFromDouble(double d) { + return (d == -1.0d || d > 0); + } + + public Boolean createFromBigInteger(BigInteger b) { + return b.compareTo(BigInteger.valueOf(0)) > 0 || b.compareTo(BigInteger.valueOf(-1)) == 0; } private static final class BooleanMySqlParameter extends AbstractMySqlParameter { diff --git a/r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/codec/BooleanCodecTest.java b/r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/codec/BooleanCodecTest.java index 85955e73..9e32bc74 100644 --- a/r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/codec/BooleanCodecTest.java +++ b/r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/codec/BooleanCodecTest.java @@ -22,6 +22,7 @@ import io.netty.buffer.Unpooled; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.nio.charset.Charset; import java.util.Arrays; @@ -70,6 +71,14 @@ void decodeString() { Decoding d2 = new Decoding(Unpooled.copiedBuffer("false", c), "false", MySqlType.VARCHAR); Decoding d3 = new Decoding(Unpooled.copiedBuffer("1", c), "1", MySqlType.VARCHAR); Decoding d4 = new Decoding(Unpooled.copiedBuffer("0", c), "0", MySqlType.VARCHAR); + Decoding d5 = new Decoding(Unpooled.copiedBuffer("Y", c), "Y", MySqlType.VARCHAR); + Decoding d6 = new Decoding(Unpooled.copiedBuffer("no", c), "no", MySqlType.VARCHAR); + Decoding d7 = new Decoding(Unpooled.copyDouble(26.57d), 26.57d, MySqlType.DOUBLE); + Decoding d8 = new Decoding(Unpooled.copyLong(-57L), -57L, MySqlType.TINYINT); + Decoding d9 = new Decoding(Unpooled.copyLong(100000L), 100000L, MySqlType.BIGINT); + Decoding d10 = new Decoding(Unpooled.copiedBuffer("-12345678901234567890", c), + "-12345678901234567890", MySqlType.VARCHAR); + Decoding d11 = new Decoding(Unpooled.copiedBuffer("Banana", c), "Banana", MySqlType.VARCHAR); assertThat(codec.decode(d1.content(), d1.metadata(), Boolean.class, false, ConnectionContextTest.mock())) .as("Decode failed, %s", d1) @@ -86,5 +95,32 @@ void decodeString() { assertThat(codec.decode(d4.content(), d4.metadata(), Boolean.class, false, ConnectionContextTest.mock())) .as("Decode failed, %s", d4) .isEqualTo(false); + + assertThat(codec.decode(d5.content(), d5.metadata(), Boolean.class, false, ConnectionContextTest.mock())) + .as("Decode failed, %s", d5) + .isEqualTo(true); + + assertThat(codec.decode(d6.content(), d6.metadata(), Boolean.class, false, ConnectionContextTest.mock())) + .as("Decode failed, %s", d6) + .isEqualTo(false); + + assertThat(codec.decode(d7.content(), d7.metadata(), Boolean.class, false, ConnectionContextTest.mock())) + .as("Decode failed, %s", d7) + .isEqualTo(true); + + assertThat(codec.decode(d8.content(), d8.metadata(), Boolean.class, false, ConnectionContextTest.mock())) + .as("Decode failed, %s", d8) + .isEqualTo(false); + + assertThat(codec.decode(d9.content(), d9.metadata(), Boolean.class, false, ConnectionContextTest.mock())) + .as("Decode failed, %s", d9) + .isEqualTo(true); + + assertThat(codec.decode(d10.content(), d10.metadata(), Boolean.class, false, ConnectionContextTest.mock())) + .as("Decode failed, %s", d10) + .isEqualTo(false); + + assertThatThrownBy(() -> {codec.decode(d11.content(), d11.metadata(), Boolean.class, false, ConnectionContextTest.mock());}) + .isInstanceOf(IllegalArgumentException.class); } }