diff --git a/clickhouse/client.cpp b/clickhouse/client.cpp index 531498c0..ccd26c49 100644 --- a/clickhouse/client.cpp +++ b/clickhouse/client.cpp @@ -904,7 +904,7 @@ bool Client::Impl::ReceiveData(Block & block) { bool Client::Impl::ReceiveException(ServerError & error) { std::shared_ptr e(new Exception); - bool has_nested = false; // obsolete: https://github.com/ClickHouse/ClickHouse/blob/ef11941cf5a/src/IO/ReadHelpers.cpp#L2017 + uint8_t has_nested = 0; // obsolete: https://github.com/ClickHouse/ClickHouse/blob/ef11941cf5a/src/IO/ReadHelpers.cpp#L2017 if (!WireFormat::ReadFixed(*input_, &e->code) || !WireFormat::ReadString(*input_, &e->name) || @@ -924,15 +924,18 @@ bool Client::Impl::ReceiveException(ServerError & error) { } bool Client::Impl::ReceiveProfileInfo(Profile & profile) { - if (!WireFormat::ReadUInt64(*input_, &profile.rows) || - !WireFormat::ReadUInt64(*input_, &profile.blocks) || - !WireFormat::ReadUInt64(*input_, &profile.bytes) || - !WireFormat::ReadFixed(*input_, &profile.applied_limit) || - !WireFormat::ReadUInt64(*input_, &profile.rows_before_limit) || - !WireFormat::ReadFixed(*input_, &profile.calculated_rows_before_limit)) { + uint8_t applied_limit = 0; + uint8_t calculated_rows_before_limit = 0; + + if (!WireFormat::ReadUInt64(*input_, &profile.rows) || !WireFormat::ReadUInt64(*input_, &profile.blocks) || + !WireFormat::ReadUInt64(*input_, &profile.bytes) || !WireFormat::ReadFixed(*input_, &applied_limit) || + !WireFormat::ReadUInt64(*input_, &profile.rows_before_limit) || !WireFormat::ReadFixed(*input_, &calculated_rows_before_limit)) { return false; } + profile.applied_limit = applied_limit != 0; + profile.calculated_rows_before_limit = calculated_rows_before_limit != 0; + if (events_) { events_->OnProfile(profile); } diff --git a/ut/client_protocol_ut.cpp b/ut/client_protocol_ut.cpp index 72cfdd5d..46859efe 100644 --- a/ut/client_protocol_ut.cpp +++ b/ut/client_protocol_ut.cpp @@ -238,3 +238,44 @@ TEST(ClientProtocol, WellFormedSelectResponseSucceeds) { })); EXPECT_EQ(blocks, 1u); } + +TEST(ClientProtocol, ProfileInfoFlagsAcceptAnyNonZeroByte) { + std::vector script = kServerHello; + const std::vector response = { + 0x06, // packet type: ServerCodes::ProfileInfo (varint) + 0x01, // rows = 1 (varint) + 0x00, // blocks = 0 (varint) + 0x02, // bytes = 2 (varint) + 0x05, // applied_limit: non-canonical true (fixed, 1 byte) + 0x03, // rows_before_limit = 3 (varint) + 0x00, // calculated_rows_before_limit = false (fixed, 1 byte) + 0x06, // packet type: ServerCodes::ProfileInfo (varint) + 0x01, 0x00, 0x02, + 0x00, // applied_limit = false + 0x03, + 0xFF, // calculated_rows_before_limit: non-canonical true + 0x05, // packet type: ServerCodes::EndOfStream (varint) + }; + script.insert(script.end(), response.begin(), response.end()); + + Client client(ScriptedClientOptions(), std::make_unique(script)); + + std::vector> flags; + Query query("SELECT 1"); + query.OnProfile([&flags](const Profile& profile) { + EXPECT_EQ(profile.rows, 1u); + EXPECT_EQ(profile.bytes, 2u); + EXPECT_EQ(profile.rows_before_limit, 3u); + // Inspect the object representation: a bool holding anything other + // than 0 or 1 is undefined behaviour to load. + flags.emplace_back(*reinterpret_cast(&profile.applied_limit), + *reinterpret_cast(&profile.calculated_rows_before_limit)); + }); + client.Select(query); + + ASSERT_EQ(flags.size(), 2u); + EXPECT_EQ(flags[0].first, 1u); + EXPECT_EQ(flags[0].second, 0u); + EXPECT_EQ(flags[1].first, 0u); + EXPECT_EQ(flags[1].second, 1u); +}