Skip to content

Commit 742f717

Browse files
committed
Fix handling of TINYINT(1) UNSIGNED when tinyInt1isBit is set
Motivation: When the `tinyInt1isBit` flag is set, attempting to convert `TINYINT(1) UNSIGNED` to a boolean results in immediate rejection. Modifications: Prevent conversion of `TINYINT(1) UNSIGNED` to boolean when `tinyInt1isBit` is enabled. Result: `TINYINT(1) UNSIGNED` is handled correctly without unnecessary conversion attempts.
1 parent e211cd8 commit 742f717

File tree

5 files changed

+36
-15
lines changed

5 files changed

+36
-15
lines changed

Diff for: r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/MySqlConnectionConfiguration.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -1220,8 +1220,10 @@ public Builder metrics(boolean enabled) {
12201220

12211221
/**
12221222
* Option to whether the driver should interpret MySQL's TINYINT(1) as a BIT type.
1223-
* When enabled, TINYINT(1) columns (both SIGNED and UNSIGNED) will be treated as
1224-
* BIT. default to {@code true}.
1223+
* When enabled, TINYINT(1) columns will be treated as BIT. Defaults to {@code true}.
1224+
* <p>
1225+
* Note: Only singed TINYINT(1) columns can be treated as BIT or Boolean.
1226+
* Ref: https://bugs.mysql.com/bug.php?id=100309
12251227
*
12261228
* @param tinyInt1isBit {@code true} to treat TINYINT(1) as BIT
12271229
* @return this {@link Builder}

Diff for: r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/MySqlConnectionFactoryProvider.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,10 @@ public final class MySqlConnectionFactoryProvider implements ConnectionFactoryPr
332332

333333
/**
334334
* Option to whether the driver should interpret MySQL's TINYINT(1) as a BIT type.
335-
* When enabled, TINYINT(1) columns (both SIGNED and UNSIGNED) will be treated as
336-
* BIT. default to {@code true}.
335+
* When enabled, TINYINT(1) columns will be treated as BIT. Defaults to {@code true}.
336+
* <p>
337+
* Note: Only singed TINYINT(1) columns can be treated as BIT or Boolean.
338+
* Ref: https://bugs.mysql.com/bug.php?id=100309
337339
*
338340
* @since 1.4.0
339341
*/

Diff for: r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
*/
3333
final class BooleanCodec extends AbstractPrimitiveCodec<Boolean> {
3434

35+
private static final Integer INTEGER_ONE = Integer.valueOf(1);
36+
3537
static final BooleanCodec INSTANCE = new BooleanCodec();
3638

3739
private BooleanCodec() {
@@ -86,7 +88,7 @@ public MySqlParameter encode(Object value, CodecContext context) {
8688
public boolean doCanDecode(MySqlReadableMetadata metadata) {
8789
MySqlType type = metadata.getType();
8890
return ((type == MySqlType.BIT || type == MySqlType.TINYINT) &&
89-
Integer.valueOf(1).equals(metadata.getPrecision())) || type == MySqlType.VARCHAR;
91+
INTEGER_ONE.equals(metadata.getPrecision())) || type == MySqlType.VARCHAR;
9092
}
9193

9294
public Boolean createFromLong(long l) {

Diff for: r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java

+12-10
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@
4545
*/
4646
final class DefaultCodecs implements Codecs {
4747

48-
private static final Integer INTEGER_ONE = Integer.valueOf(1);
49-
5048
private static final List<Codec<?>> DEFAULT_CODECS = InternalArrays.asImmutableList(
5149
ByteCodec.INSTANCE,
5250
ShortCodec.INSTANCE,
@@ -369,18 +367,22 @@ private static Class<?> chooseClass(final MySqlReadableMetadata metadata, Class<
369367
return type.isAssignableFrom(javaType) ? javaType : type;
370368
}
371369

372-
private static Class<?> getDefaultJavaType(final MySqlReadableMetadata metadata, final CodecContext codecContext) {
373-
final MySqlType type = metadata.getType();
374-
final Integer precision = metadata.getPrecision();
375370

376-
if (INTEGER_ONE.equals(precision) && (type == MySqlType.TINYINT || type == MySqlType.TINYINT_UNSIGNED)
377-
&& codecContext.isTinyInt1isBit()) {
378-
return Boolean.class;
371+
private static boolean shouldBeTreatedAsBoolean(final @Nullable Integer precision, final MySqlType type,
372+
final CodecContext context) {
373+
if (precision == null || precision != 1) {
374+
return false;
379375
}
380-
381376
// ref: https://github.com/asyncer-io/r2dbc-mysql/issues/277
382377
// BIT(1) should be treated as Boolean by default.
383-
if (INTEGER_ONE.equals(precision) && type == MySqlType.BIT) {
378+
return type == MySqlType.BIT || type == MySqlType.TINYINT && context.isTinyInt1isBit();
379+
}
380+
381+
private static Class<?> getDefaultJavaType(final MySqlReadableMetadata metadata, final CodecContext codecContext) {
382+
final MySqlType type = metadata.getType();
383+
final Integer precision = metadata.getPrecision();
384+
385+
if (shouldBeTreatedAsBoolean(precision, type, codecContext)) {
384386
return Boolean.class;
385387
}
386388

Diff for: r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/ConnectionIntegrationTest.java

+13
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,19 @@ public void tinyInt1isBitTrueTestValue1() {
592592
);
593593
}
594594

595+
@Test
596+
public void tinyInt1isBitTrueTestUnsignedTinyInt1isNotBoolean() {
597+
complete(connection -> Mono.from(connection.createStatement("CREATE TEMPORARY TABLE `test` (`id` INT NOT NULL PRIMARY KEY, `value` TINYINT(1) UNSIGNED)").execute())
598+
.flatMap(IntegrationTestSupport::extractRowsUpdated)
599+
.thenMany(connection.createStatement("INSERT INTO `test` VALUES (1, 1)").execute())
600+
.flatMap(IntegrationTestSupport::extractRowsUpdated)
601+
.thenMany(connection.createStatement("SELECT `value` FROM `test`").execute())
602+
.flatMap(result -> result.map((row, metadata) -> row.get("value", Object.class)))
603+
.doOnNext(value -> assertThat(value).isInstanceOf(Short.class))
604+
.doOnNext(value -> assertThat(value).isEqualTo(Short.valueOf((short)1)))
605+
);
606+
}
607+
595608
@Test
596609
public void tinyInt1isBitTrueTestValue0() {
597610
complete(connection -> Mono.from(connection.createStatement("CREATE TEMPORARY TABLE `test` (`id` INT NOT NULL PRIMARY KEY, `value` TINYINT(1))").execute())

0 commit comments

Comments
 (0)