keys: do not use zip_iterator for printing key components

boost's the operator==() implementation of boost's zip_iterator
returns true only if all elements in enclosed tuple of zip_iterator
are equal. and the zip_iterator always advances all the iterators in
the enclosed tuple. but in our case, some components might be missing.
in other words, the size of the `components` might be smaller than
that of the `types` range. so, when the zip_iterator advances past
the end of the components, scylla starts reading out of bounds.

because zip_iterator does not allow us to customize how it implements
the equal operator. and we cannot deduce the size of components without
reading all of them. so in this change, we partially revert
3738fcbe05, instead of using fmt::join(),
just iterate through the components manually. this should avoid
the out-of-bound reading, and also preserve the original behavior.

Branches: 5.3
Fixes #14435
Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>

Closes #14457
This commit is contained in:
Kefu Chai
2023-06-30 12:58:57 +08:00
committed by Avi Kivity
parent d88dfa0ad2
commit 1ab2bb69b8

38
keys.hh
View File

@@ -16,9 +16,6 @@
#include "utils/utf8.hh"
#include "replica/database_fwd.hh"
#include "schema/schema_fwd.hh"
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <compare>
#include <span>
@@ -773,26 +770,23 @@ namespace detail {
template <typename WithSchemaWrapper, typename FormatContext>
auto format_pk(const WithSchemaWrapper& pk, FormatContext& ctx) {
const auto& [schema, key] = pk;
const auto& types = key.get_compound_type(schema)->types();
const auto components = key.components(schema);
return fmt::format_to(
ctx.out(), "{}",
fmt::join(boost::make_iterator_range(
boost::make_zip_iterator(boost::make_tuple(types.begin(),
components.begin())),
boost::make_zip_iterator(boost::make_tuple(types.end(),
components.end()))) |
boost::adaptors::transformed([](const auto& type_and_component) {
auto& [type, component] = type_and_component;
auto key = type->to_string(to_bytes(component));
if (!utils::utf8::validate((const uint8_t *) key.data(), key.size())) {
return sstring("<non-utf8-key>");
}
return key;
}),
":"));
auto type_iterator = key.get_compound_type(schema)->types().begin();
bool first = true;
auto out = ctx.out();
for (auto&& component : key.components(schema)) {
if (!first) {
out = fmt::format_to(out, "{}", ":");
}
first = false;
auto key = (*type_iterator++)->to_string(to_bytes(component));
if (utils::utf8::validate((const uint8_t *) key.data(), key.size())) {
out = fmt::format_to(out, "{}", key);
} else {
out = fmt::format_to(out, "{}", "<non-utf8-key>");
}
}
return out;
}
} // namespace detail
template <>