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
Original file line number Diff line number Diff line change
Expand Up @@ -1220,8 +1220,10 @@ public Builder metrics(boolean enabled) {

/**
* Option to whether the driver should interpret MySQL's TINYINT(1) as a BIT type.
* When enabled, TINYINT(1) columns (both SIGNED and UNSIGNED) will be treated as
* BIT. default to {@code true}.
* When enabled, TINYINT(1) columns will be treated as BIT. Defaults to {@code true}.
* <p>
* Note: Only signed TINYINT(1) columns can be treated as BIT or Boolean.
* Ref: https://bugs.mysql.com/bug.php?id=100309
*
* @param tinyInt1isBit {@code true} to treat TINYINT(1) as BIT
* @return this {@link Builder}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,10 @@ public final class MySqlConnectionFactoryProvider implements ConnectionFactoryPr

/**
* Option to whether the driver should interpret MySQL's TINYINT(1) as a BIT type.
* When enabled, TINYINT(1) columns (both SIGNED and UNSIGNED) will be treated as
* BIT. default to {@code true}.
* When enabled, TINYINT(1) columns will be treated as BIT. Defaults to {@code true}.
* <p>
* Note: Only signed TINYINT(1) columns can be treated as BIT or Boolean.
* Ref: https://bugs.mysql.com/bug.php?id=100309
*
* @since 1.4.0
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
*/
final class BooleanCodec extends AbstractPrimitiveCodec<Boolean> {

private static final Integer INTEGER_ONE = Integer.valueOf(1);

static final BooleanCodec INSTANCE = new BooleanCodec();

private BooleanCodec() {
Expand Down Expand Up @@ -86,7 +88,7 @@ public MySqlParameter encode(Object value, CodecContext context) {
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;
INTEGER_ONE.equals(metadata.getPrecision())) || type == MySqlType.VARCHAR;
}

public Boolean createFromLong(long l) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@
*/
final class DefaultCodecs implements Codecs {

private static final Integer INTEGER_ONE = Integer.valueOf(1);

private static final List<Codec<?>> DEFAULT_CODECS = InternalArrays.asImmutableList(
ByteCodec.INSTANCE,
ShortCodec.INSTANCE,
Expand Down Expand Up @@ -369,18 +367,22 @@ private static Class<?> chooseClass(final MySqlReadableMetadata metadata, Class<
return type.isAssignableFrom(javaType) ? javaType : type;
}

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

if (INTEGER_ONE.equals(precision) && (type == MySqlType.TINYINT || type == MySqlType.TINYINT_UNSIGNED)
&& codecContext.isTinyInt1isBit()) {
return Boolean.class;
private static boolean shouldBeTreatedAsBoolean(final @Nullable Integer precision, final MySqlType type,
final CodecContext context) {
if (precision == null || precision != 1) {
return false;
}

// ref: https://github.com/asyncer-io/r2dbc-mysql/issues/277
// BIT(1) should be treated as Boolean by default.
if (INTEGER_ONE.equals(precision) && type == MySqlType.BIT) {
return type == MySqlType.BIT || type == MySqlType.TINYINT && context.isTinyInt1isBit();
}

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

if (shouldBeTreatedAsBoolean(precision, type, codecContext)) {
return Boolean.class;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,19 @@ public void tinyInt1isBitTrueTestValue1() {
);
}

@Test
public void tinyInt1isBitTrueTestUnsignedTinyInt1isNotBoolean() {
complete(connection -> Mono.from(connection.createStatement("CREATE TEMPORARY TABLE `test` (`id` INT NOT NULL PRIMARY KEY, `value` TINYINT(1) UNSIGNED)").execute())
.flatMap(IntegrationTestSupport::extractRowsUpdated)
.thenMany(connection.createStatement("INSERT INTO `test` VALUES (1, 1)").execute())
.flatMap(IntegrationTestSupport::extractRowsUpdated)
.thenMany(connection.createStatement("SELECT `value` FROM `test`").execute())
.flatMap(result -> result.map((row, metadata) -> row.get("value", Object.class)))
.doOnNext(value -> assertThat(value).isInstanceOf(Short.class))
.doOnNext(value -> assertThat(value).isEqualTo(Short.valueOf((short)1)))
);
}

@Test
public void tinyInt1isBitTrueTestValue0() {
complete(connection -> Mono.from(connection.createStatement("CREATE TEMPORARY TABLE `test` (`id` INT NOT NULL PRIMARY KEY, `value` TINYINT(1))").execute())
Expand Down