Merge pull request #55427 from slvrtrn/mysql-boolean-format-fix

Fix MySQL interface boolean representation
This commit is contained in:
Robert Schulze 2023-10-10 13:11:33 +02:00 committed by GitHub
commit 24fecaeb7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 22 deletions

View File

@ -50,10 +50,7 @@ ResultSetRow::ResultSetRow(const Serializations & serializations_, const DataTyp
payload_size += 1;
break;
case TypeIndex::UInt8:
if (data_type->getName() == "Bool")
payload_size += 2; // BIT MySQL type is string<lenenc> in binary
else
payload_size += 1;
payload_size += 1;
break;
case TypeIndex::Int16:
case TypeIndex::UInt16:
@ -168,8 +165,6 @@ void ResultSetRow::writePayloadImpl(WriteBuffer & buffer) const
}
case TypeIndex::UInt8: {
UInt8 value = assert_cast<const ColumnVector<UInt8> &>(*col).getData()[row_num];
if (data_type->getName() == "Bool")
buffer.write(static_cast<char>(1));
buffer.write(reinterpret_cast<char *>(&value), 1);
break;
}

View File

@ -20,6 +20,7 @@ namespace ProtocolText
ResultSetRow::ResultSetRow(const Serializations & serializations, const Columns & columns_, int row_num_)
: columns(columns_), row_num(row_num_)
{
static FormatSettings format_settings = {.bool_true_representation = "1", .bool_false_representation = "0"};
for (size_t i = 0; i < columns.size(); ++i)
{
if (columns[i]->isNullAt(row_num))
@ -30,7 +31,7 @@ ResultSetRow::ResultSetRow(const Serializations & serializations, const Columns
else
{
WriteBufferFromOwnString ostr;
serializations[i]->serializeText(*columns[i], row_num, ostr, FormatSettings());
serializations[i]->serializeText(*columns[i], row_num, ostr, format_settings);
payload_size += getLengthEncodedStringSize(ostr.str());
serialized.push_back(std::move(ostr.str()));
}
@ -45,12 +46,10 @@ size_t ResultSetRow::getPayloadSize() const
void ResultSetRow::writePayloadImpl(WriteBuffer & buffer) const
{
for (size_t i = 0; i < columns.size(); ++i)
{
if (columns[i]->isNullAt(row_num))
buffer.write(serialized[i].data(), 1);
else
writeLengthEncodedString(serialized[i], buffer);
}
}
void ComFieldList::readPayloadImpl(ReadBuffer & payload)
@ -142,19 +141,13 @@ ColumnDefinition getColumnDefinition(const String & column_name, const DataTypeP
CharacterSet charset = CharacterSet::binary;
int flags = 0;
uint8_t decimals = 0;
TypeIndex type_index = removeLowCardinality(removeNullable(data_type))->getTypeId();
DataTypePtr normalized_data_type = removeLowCardinality(removeNullable(data_type));
TypeIndex type_index = normalized_data_type->getTypeId();
switch (type_index)
{
case TypeIndex::UInt8:
if (data_type->getName() == "Bool")
{
column_type = ColumnType::MYSQL_TYPE_BIT;
}
else
{
column_type = ColumnType::MYSQL_TYPE_TINY;
flags = ColumnDefinitionFlags::BINARY_FLAG | ColumnDefinitionFlags::UNSIGNED_FLAG;
}
column_type = ColumnType::MYSQL_TYPE_TINY;
flags = ColumnDefinitionFlags::BINARY_FLAG | ColumnDefinitionFlags::UNSIGNED_FLAG;
break;
case TypeIndex::UInt16:
column_type = ColumnType::MYSQL_TYPE_SHORT;
@ -213,7 +206,7 @@ ColumnDefinition getColumnDefinition(const String & column_name, const DataTypeP
// MySQL Decimal has max 65 precision and 30 scale
// Decimal256 (min scale is 39) is higher than the MySQL supported range and handled in the default case
// See https://dev.mysql.com/doc/refman/8.0/en/precision-math-decimal-characteristics.html
const auto & type = assert_cast<const DataTypeDecimal128 &>(*data_type);
const auto & type = assert_cast<const DataTypeDecimal128 &>(*normalized_data_type);
if (type.getPrecision() > 65 || type.getScale() > 30)
{
column_type = ColumnType::MYSQL_TYPE_STRING;

View File

@ -14,7 +14,7 @@ ui128 type is CHAR, value: 15324355
ui256 type is CHAR, value: 41345135123432
f32 type is FLOAT, value: -0.796896
f64 type is DOUBLE, value: -0.113259
b type is BIT, value: true
b type is TINYINT, value: true
Row #2
i8 type is TINYINT, value: 127
i16 type is SMALLINT, value: 32767
@ -30,7 +30,7 @@ ui128 type is CHAR, value: 340282366920938463463374607431768211455
ui256 type is CHAR, value: 115792089237316195423570985008687907853269984665640564039457584007913129639935
f32 type is FLOAT, value: 1.234000
f64 type is DOUBLE, value: 3.352451
b type is BIT, value: false
b type is TINYINT, value: false
### testStringTypes
Row #1

View File

@ -577,6 +577,37 @@ def test_mysql_set_variables(started_cluster):
assert code == 0
def test_mysql_boolean_format(started_cluster):
node.query(
"""
CREATE OR REPLACE TABLE mysql_boolean_format_test
(
`a` Bool,
`b` Nullable(Bool),
`c` LowCardinality(Nullable(Bool))
) ENGINE MergeTree ORDER BY a;
""",
settings={"password": "123", "allow_suspicious_low_cardinality_types": 1},
)
node.query(
"INSERT INTO mysql_boolean_format_test VALUES (false, true, false), (true, false, true);",
settings={"password": "123"},
)
code, (stdout, stderr) = started_cluster.mysql_client_container.exec_run(
"""
mysql --protocol tcp -h {host} -P {port} default -u user_with_double_sha1 --password=abacaba
-e "SELECT * FROM mysql_boolean_format_test;"
""".format(
host=started_cluster.get_instance_ip("node"), port=server_port
),
demux=True,
)
logging.debug(
f"test_mysql_boolean_format code:{code} stdout:{stdout}, stderr:{stderr}"
)
assert stdout.decode() == "a\tb\tc\n" + "0\t1\t0\n" + "1\t0\t1\n"
def test_python_client(started_cluster):
client = pymysql.connections.Connection(
host=started_cluster.get_instance_ip("node"),