cql3/expr: use utils::chunked_string for untyped_constant::raw_text

This value can be a string or bytes literal, which can get very large in
rare cases. Use chunked storage to avoid large allocations.
This commit is contained in:
Botond Dénes
2026-02-12 09:21:29 +02:00
parent 2c9a5f9634
commit 4af3359744
4 changed files with 24 additions and 18 deletions

View File

@@ -1840,7 +1840,7 @@ property[cql3::statements::property_definitions& props]
;
propertyValue returns [sstring str]
: c=constant { $str = c.raw_text; }
: c=constant { $str = c.raw_text.linearize(); }
// FIXME: unreserved keywords below are indistinguishable from their string representation,
// which might be problematic in the future. A possible solution is to use a more complicated
// type for storing property values instead of just plain strings. For the specific case

View File

@@ -784,9 +784,9 @@ auto fmt::formatter<cql3::expr::expression::printer>::format(const cql3::expr::e
},
[&] (const untyped_constant& uc) {
if (uc.partial_type == untyped_constant::type_class::string) {
out = fmt::format_to(out, "'{}'", uc.raw_text);
out = fmt::format_to(out, "'{}'", uc.raw_text.linearize());
} else {
out = fmt::format_to(out, "{}", uc.raw_text);
out = fmt::format_to(out, "{}", uc.raw_text.linearize());
}
},
[&] (const tuple_constructor& tc) {
@@ -2668,8 +2668,10 @@ std::map<sstring, sstring> convert_property_map(const collection_constructor& ma
add_recognition_error(msg);
break;
}
if (!res.emplace(left->raw_text, right->raw_text).second) {
sstring msg = fmt::format("Multiple definition for property {}", left->raw_text);
const auto left_text = left->raw_text.linearize();
const auto right_text = right->raw_text.linearize();
if (!res.emplace(left_text, right_text).second) {
sstring msg = fmt::format("Multiple definition for property {}", left_text);
add_recognition_error(msg);
break;
}
@@ -2702,8 +2704,10 @@ convert_extended_property_map(const collection_constructor& map, error_sink_fn a
}
auto right_str = expr::as_if<untyped_constant>(&entry_tuple->elements[1]);
if (right_str) {
if (!res.emplace(left->raw_text, right_str->raw_text).second) {
sstring msg = fmt::format("Multiple definition for property {}", left->raw_text);
const auto left_text = left->raw_text.linearize();
const auto right_text = right_str->raw_text.linearize();
if (!res.emplace(left_text, right_text).second) {
sstring msg = fmt::format("Multiple definition for property {}", left_text);
add_recognition_error(msg);
break;
}
@@ -2727,10 +2731,11 @@ convert_extended_property_map(const collection_constructor& map, error_sink_fn a
add_recognition_error(msg);
return "<invalid>";
}
return elem->raw_text;
return elem->raw_text.linearize();
}) | std::ranges::to<std::vector<sstring>>();
if (!res.emplace(left->raw_text, std::move(values)).second) {
sstring msg = fmt::format("Multiple definition for property {}", left->raw_text);
const auto left_text = left->raw_text.linearize();
if (!res.emplace(left_text, std::move(values)).second) {
sstring msg = fmt::format("Multiple definition for property {}", left_text);
add_recognition_error(msg);
break;
}

View File

@@ -19,6 +19,7 @@
#include "cql3/functions/function_name.hh"
#include "seastarx.hh"
#include "cql3/values.hh"
#include "utils/chunked_string.hh"
class row;
@@ -385,7 +386,7 @@ struct bind_variable {
struct untyped_constant {
enum type_class { integer, floating_point, string, boolean, duration, uuid, hex, null };
type_class partial_type;
sstring raw_text;
utils::chunked_string raw_text;
friend bool operator==(const untyped_constant&, const untyped_constant&) = default;
};

View File

@@ -702,19 +702,19 @@ template <> struct fmt::formatter<cql3::expr::untyped_constant::type_class> : fm
namespace cql3::expr {
static
bytes
untyped_constant_parsed_value(const untyped_constant uc, data_type validator)
managed_bytes
untyped_constant_parsed_value(const untyped_constant& uc, data_type validator)
{
try {
if (uc.partial_type == untyped_constant::type_class::hex && validator == bytes_type) {
auto v = static_cast<std::string_view>(uc.raw_text);
auto v = utils::chunked_string_view(uc.raw_text);
v.remove_prefix(2);
return to_bytes(validator->from_string(v));
return validator->from_string(v);
}
if (validator->is_counter()) {
return to_bytes(long_type->from_string(uc.raw_text));
return long_type->from_string(uc.raw_text);
}
return to_bytes(validator->from_string(uc.raw_text));
return validator->from_string(uc.raw_text);
} catch (const marshal_exception& e) {
throw exceptions::invalid_request_exception(e.what());
}
@@ -810,7 +810,7 @@ untyped_constant_prepare_expression(const untyped_constant& uc, data_dictionary:
if (!is_assignable(untyped_constant_test_assignment(uc, db, keyspace, *receiver))) {
if (uc.partial_type != untyped_constant::type_class::null) {
throw exceptions::invalid_request_exception(format("Invalid {} constant ({}) for \"{}\" of type {}",
uc.partial_type, uc.raw_text, *receiver->name, receiver->type->as_cql3_type().to_string()));
uc.partial_type, uc.raw_text.linearize(), *receiver->name, receiver->type->as_cql3_type().to_string()));
} else {
throw exceptions::invalid_request_exception("Invalid null value for counter increment/decrement");
}