mirror of
https://github.com/scylladb/scylladb.git
synced 2026-05-30 19:46:48 +00:00
Read query as fragmented string from the input stream in transport/server.cc, propagate it a such to query_processor::prepare() and also store it as such in cql3::cql_statement::raw_cql_statement. Unfortunately, the query still has to be linearized for parsing, as ANTLR -- although allows for custom InputStream implementation -- plays pointer arithmetics games with the pointers obtained from them, so fragmented input cannot be used. To amortize the cost of this linearization, the query string is linearized through utils::reusable_buffer. The parser can be invoked recursively, nested invokations linearize directly. Still, this patch limits the places where the query is linearized to the following: * Parsing * Audit * Logs and error messages So the normal query paths for queries that actually can get arbitrarily large (UPDATE and INSERT) should only linearize the query temporarily for parsing.
473 lines
20 KiB
C++
473 lines
20 KiB
C++
/*
|
|
* Copyright (C) 2018-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "server.hh"
|
|
#include "utils/utf8.hh"
|
|
#include "utils/fragmented_temporary_buffer.hh"
|
|
#include "utils/result.hh"
|
|
#include "utils/chunked_string.hh"
|
|
|
|
namespace cql_transport {
|
|
|
|
struct unset_tag {};
|
|
|
|
struct value_view_and_unset {
|
|
cql3::raw_value_view value;
|
|
bool unset = false;
|
|
|
|
value_view_and_unset(cql3::raw_value_view value) : value(std::move(value)) {}
|
|
value_view_and_unset(unset_tag) : value(cql3::raw_value_view::make_null()), unset(true) {}
|
|
};
|
|
|
|
class request_reader {
|
|
fragmented_temporary_buffer::istream _in;
|
|
bytes_ostream* _linearization_buffer;
|
|
private:
|
|
struct exception_creator {
|
|
[[gnu::cold]]
|
|
static utils::result_with_exception_ptr<void> out_of_range(size_t attempted_read, size_t actual_left) {
|
|
return bo::failure(std::make_exception_ptr(exceptions::protocol_exception(format("truncated frame: expected {:d} bytes, length is {:d}", attempted_read, actual_left))));
|
|
}
|
|
};
|
|
static utils::result_with_exception_ptr<void> validate_utf8(std::string_view s) {
|
|
auto error_pos = utils::utf8::validate_with_error_position(to_bytes_view(s));
|
|
if (error_pos) {
|
|
return bo::failure(exceptions::protocol_exception(format("Cannot decode string as UTF8, invalid character at byte offset {}", *error_pos)));
|
|
}
|
|
return bo::success();
|
|
}
|
|
|
|
template<FragmentedView View>
|
|
static utils::result_with_exception_ptr<void> validate_utf8_fragmented(View fv) {
|
|
auto error_pos = utils::utf8::validate_with_error_position_fragmented(fv);
|
|
if (error_pos) {
|
|
return bo::failure(exceptions::protocol_exception(format("Cannot decode string as UTF8, invalid character at byte offset {}", *error_pos)));
|
|
}
|
|
return bo::success();
|
|
}
|
|
|
|
static utils::result_with_exception_ptr<db::consistency_level> wire_to_consistency(int16_t v) {
|
|
switch (v) {
|
|
case 0x0000: return db::consistency_level::ANY;
|
|
case 0x0001: return db::consistency_level::ONE;
|
|
case 0x0002: return db::consistency_level::TWO;
|
|
case 0x0003: return db::consistency_level::THREE;
|
|
case 0x0004: return db::consistency_level::QUORUM;
|
|
case 0x0005: return db::consistency_level::ALL;
|
|
case 0x0006: return db::consistency_level::LOCAL_QUORUM;
|
|
case 0x0007: return db::consistency_level::EACH_QUORUM;
|
|
case 0x0008: return db::consistency_level::SERIAL;
|
|
case 0x0009: return db::consistency_level::LOCAL_SERIAL;
|
|
case 0x000A: return db::consistency_level::LOCAL_ONE;
|
|
default: return bo::failure(std::make_exception_ptr(exceptions::protocol_exception(format("Unknown code {:d} for a consistency level", v))));
|
|
}
|
|
}
|
|
public:
|
|
explicit request_reader(fragmented_temporary_buffer::istream in, bytes_ostream& linearization_buffer) noexcept
|
|
: _in(std::move(in))
|
|
, _linearization_buffer(&linearization_buffer)
|
|
{ }
|
|
|
|
fragmented_temporary_buffer::istream get_stream() {
|
|
return _in;
|
|
}
|
|
|
|
size_t bytes_left() const {
|
|
return _in.bytes_left();
|
|
}
|
|
|
|
utils::result_with_exception_ptr<bytes_view> read_raw_bytes_view(size_t n) {
|
|
return _in.read_bytes_view(n, *_linearization_buffer, exception_creator());
|
|
}
|
|
|
|
utils::result_with_exception_ptr<int8_t> read_byte() {
|
|
utils::result_with_exception_ptr<int8_t> v = _in.read<int8_t>(exception_creator());
|
|
if (!v) [[unlikely]] {
|
|
return bo::failure(std::move(v).assume_error());
|
|
}
|
|
return be_to_cpu(std::move(v).assume_value());
|
|
}
|
|
|
|
utils::result_with_exception_ptr<int32_t> read_int() {
|
|
utils::result_with_exception_ptr<int32_t> v = _in.read<int32_t>(exception_creator());
|
|
if (!v) [[unlikely]] {
|
|
return bo::failure(std::move(v).assume_error());
|
|
}
|
|
return be_to_cpu(std::move(v).assume_value());
|
|
}
|
|
|
|
utils::result_with_exception_ptr<int64_t> read_long() {
|
|
utils::result_with_exception_ptr<int64_t> v = _in.read<int64_t>(exception_creator());
|
|
if (!v) [[unlikely]] {
|
|
return bo::failure(std::move(v).assume_error());
|
|
}
|
|
return be_to_cpu(std::move(v).assume_value());
|
|
}
|
|
|
|
utils::result_with_exception_ptr<uint16_t> read_short() {
|
|
utils::result_with_exception_ptr<uint16_t> v = _in.read<uint16_t>(exception_creator());
|
|
if (!v) [[unlikely]] {
|
|
return bo::failure(std::move(v).assume_error());
|
|
}
|
|
return be_to_cpu(v.assume_value());
|
|
}
|
|
|
|
utils::result_with_exception_ptr<sstring> read_string() {
|
|
utils::result_with_exception_ptr<uint16_t> n = read_short();
|
|
if (!n) [[unlikely]] {
|
|
return bo::failure(std::move(n).assume_error());
|
|
}
|
|
sstring s = uninitialized_string(n.assume_value());
|
|
auto output = _in.read_to(n.assume_value(), s.begin(), exception_creator());
|
|
if (!output) [[unlikely]] {
|
|
return bo::failure(std::move(output).assume_error());
|
|
}
|
|
utils::result_with_exception_ptr<void> check = validate_utf8(s);
|
|
if (!check) [[unlikely]] {
|
|
return bo::failure(std::move(check).assume_error());
|
|
}
|
|
return s;
|
|
}
|
|
|
|
utils::result_with_exception_ptr<std::string_view> read_string_view() {
|
|
utils::result_with_exception_ptr<uint16_t> n = read_short();
|
|
if (!n) [[unlikely]] {
|
|
return bo::failure(std::move(n).assume_error());
|
|
}
|
|
utils::result_with_exception_ptr<bytes_view> bv = _in.read_bytes_view(n.assume_value(), *_linearization_buffer, exception_creator());
|
|
if (!bv) [[unlikely]] {
|
|
return bo::failure(std::move(bv).assume_error());
|
|
}
|
|
auto s = std::string_view(reinterpret_cast<const char*>(bv.assume_value().data()), bv.assume_value().size());
|
|
utils::result_with_exception_ptr<void> check = validate_utf8(s);
|
|
if (!check) [[unlikely]] {
|
|
return bo::failure(std::move(check).assume_error());
|
|
}
|
|
return s;
|
|
}
|
|
|
|
utils::result_with_exception_ptr<std::string_view> read_long_string_view() {
|
|
utils::result_with_exception_ptr<int32_t> n = read_int();
|
|
if (!n) [[unlikely]] {
|
|
return bo::failure(std::move(n).assume_error());
|
|
}
|
|
utils::result_with_exception_ptr<bytes_view> bv = _in.read_bytes_view(n.assume_value(), *_linearization_buffer, exception_creator());
|
|
if (!bv) [[unlikely]] {
|
|
return bo::failure(std::move(bv).assume_error());
|
|
}
|
|
auto s = std::string_view(reinterpret_cast<const char*>(bv.assume_value().data()), bv.assume_value().size());
|
|
utils::result_with_exception_ptr<void> check = validate_utf8(s);
|
|
if (!check) [[unlikely]] {
|
|
return bo::failure(std::move(check).assume_error());
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// Read a long string directly into chunked_string without linearization
|
|
utils::result_with_exception_ptr<utils::chunked_string> read_long_chunked_string() {
|
|
utils::result_with_exception_ptr<int32_t> n = read_int();
|
|
if (!n) [[unlikely]] {
|
|
return bo::failure(std::move(n).assume_error());
|
|
}
|
|
utils::result_with_exception_ptr<fragmented_temporary_buffer::view> view = _in.read_view(n.assume_value(), exception_creator());
|
|
if (!view) [[unlikely]] {
|
|
return bo::failure(std::move(view).assume_error());
|
|
}
|
|
// Validate UTF-8 without linearization
|
|
utils::result_with_exception_ptr<void> check = validate_utf8_fragmented(view.assume_value());
|
|
if (!check) [[unlikely]] {
|
|
return bo::failure(std::move(check).assume_error());
|
|
}
|
|
// Write the fragmented view into managed_bytes
|
|
managed_bytes mb(managed_bytes::initialized_later(), n.assume_value());
|
|
managed_bytes_mutable_view dest(mb);
|
|
write_fragmented(dest, view.assume_value());
|
|
return utils::chunked_string(std::move(mb));
|
|
}
|
|
|
|
utils::result_with_exception_ptr<bytes> read_bytes() {
|
|
utils::result_with_exception_ptr<int32_t> len = read_int();
|
|
if (!len) [[unlikely]] {
|
|
return bo::failure(std::move(len).assume_error());
|
|
}
|
|
if (len.assume_value() < 0) {
|
|
return {bytes(bytes::initialized_later(), 0)};
|
|
}
|
|
bytes b(bytes::initialized_later(), len.assume_value());
|
|
auto output = _in.read_to(len.assume_value(), b.begin(), exception_creator());
|
|
if (!output) [[unlikely]] {
|
|
return bo::failure(std::move(output).assume_error());
|
|
}
|
|
return {std::move(b)};
|
|
}
|
|
|
|
utils::result_with_exception_ptr<bytes> read_short_bytes() {
|
|
utils::result_with_exception_ptr<uint16_t> n = read_short();
|
|
if (!n) [[unlikely]] {
|
|
return bo::failure(std::move(n).assume_error());
|
|
}
|
|
bytes b(bytes::initialized_later(), n.assume_value());
|
|
auto output = _in.read_to(n.assume_value(), b.begin(), exception_creator());
|
|
if (!output) [[unlikely]] {
|
|
return bo::failure(std::move(output).assume_error());
|
|
}
|
|
return b;
|
|
}
|
|
|
|
utils::result_with_exception_ptr<value_view_and_unset> read_value_view(uint8_t version) {
|
|
utils::result_with_exception_ptr<int32_t> len = read_int();
|
|
if (!len) [[unlikely]] {
|
|
return bo::failure(std::move(len).assume_error());
|
|
}
|
|
if (len.assume_value() < 0) {
|
|
if (version < 4) {
|
|
return bo::success(cql3::raw_value_view::make_null());
|
|
}
|
|
if (len.assume_value() == -1) {
|
|
return bo::success(cql3::raw_value_view::make_null());
|
|
} else if (len.assume_value() == -2) {
|
|
return bo::success(value_view_and_unset(unset_tag()));
|
|
} else {
|
|
return std::make_exception_ptr(exceptions::protocol_exception(format("invalid value length: {:d}", len.assume_value())));
|
|
}
|
|
}
|
|
utils::result_with_exception_ptr<fragmented_temporary_buffer::view> view = _in.read_view(len.assume_value(), exception_creator());
|
|
if (!view) [[unlikely]] {
|
|
return bo::failure(std::move(view).assume_error());
|
|
}
|
|
return bo::success(cql3::raw_value_view::make_value(std::move(view).assume_value()));
|
|
}
|
|
|
|
utils::result_with_exception_ptr<void> read_name_and_value_list(uint8_t version, std::vector<std::string_view>& names, std::vector<cql3::raw_value_view>& values,
|
|
cql3::unset_bind_variable_vector& unset) {
|
|
utils::result_with_exception_ptr<uint16_t> size = read_short();
|
|
if (!size) [[unlikely]] {
|
|
return bo::failure(std::move(size).assume_error());
|
|
}
|
|
names.reserve(size.assume_value());
|
|
unset.reserve(size.assume_value());
|
|
values.reserve(size.assume_value());
|
|
for (uint16_t i = 0; i < size.assume_value(); i++) {
|
|
auto name = read_string_view();
|
|
if (!name) [[unlikely]] {
|
|
return bo::failure(std::move(name).assume_error());
|
|
}
|
|
names.emplace_back(std::move(name).assume_value());
|
|
utils::result_with_exception_ptr<value_view_and_unset> vv = read_value_view(version);
|
|
if (!vv) [[unlikely]] {
|
|
return bo::failure(std::move(vv).assume_error());
|
|
}
|
|
auto&& [value, is_unset] = std::move(vv).assume_value();
|
|
values.emplace_back(std::move(value));
|
|
unset.emplace_back(is_unset);
|
|
}
|
|
return bo::success();
|
|
}
|
|
|
|
utils::result_with_exception_ptr<void> read_string_list(std::vector<sstring>& strings) {
|
|
utils::result_with_exception_ptr<uint16_t> size = read_short();
|
|
if (!size) [[unlikely]] {
|
|
return bo::failure(std::move(size).assume_error());
|
|
}
|
|
strings.reserve(size.assume_value());
|
|
for (uint16_t i = 0; i < size.assume_value(); i++) {
|
|
utils::result_with_exception_ptr<sstring> str = read_string();
|
|
if (!str) [[unlikely]] {
|
|
return bo::failure(std::move(str).assume_error());
|
|
}
|
|
strings.emplace_back(std::move(str).assume_value());
|
|
}
|
|
return bo::success();
|
|
}
|
|
|
|
utils::result_with_exception_ptr<void> read_value_view_list(uint8_t version, std::vector<cql3::raw_value_view>& values, cql3::unset_bind_variable_vector& unset) {
|
|
utils::result_with_exception_ptr<uint16_t> size = read_short();
|
|
if (!size) [[unlikely]] {
|
|
return bo::failure(std::move(size).assume_error());
|
|
}
|
|
values.reserve(size.assume_value());
|
|
unset.reserve(size.assume_value());
|
|
for (uint16_t i = 0; i < size.assume_value(); i++) {
|
|
utils::result_with_exception_ptr<value_view_and_unset> vv = read_value_view(version);
|
|
if (!vv) [[unlikely]] {
|
|
return bo::failure(std::move(vv).assume_error());
|
|
}
|
|
auto&& [value, is_unset] = std::move(vv).assume_value();
|
|
values.emplace_back(std::move(value));
|
|
unset.emplace_back(is_unset);
|
|
}
|
|
return bo::success();
|
|
}
|
|
|
|
utils::result_with_exception_ptr<db::consistency_level> read_consistency() {
|
|
utils::result_with_exception_ptr<uint16_t> v = read_short();
|
|
if (!v) [[unlikely]] {
|
|
return bo::failure(std::move(v).assume_error());
|
|
}
|
|
return wire_to_consistency(v.assume_value());
|
|
}
|
|
|
|
utils::result_with_exception_ptr<std::unordered_map<sstring, sstring>> read_string_map() {
|
|
std::unordered_map<sstring, sstring> string_map;
|
|
utils::result_with_exception_ptr<uint16_t> n = read_short();
|
|
if (!n) [[unlikely]] {
|
|
return bo::failure(std::move(n).assume_error());
|
|
}
|
|
for (auto i = 0; i < n.assume_value(); i++) {
|
|
utils::result_with_exception_ptr<sstring> key = read_string();
|
|
if (!key) [[unlikely]] {
|
|
return bo::failure(std::move(key).assume_error());
|
|
}
|
|
utils::result_with_exception_ptr<sstring> val = read_string();
|
|
if (!val) [[unlikely]] {
|
|
return bo::failure(std::move(val).assume_error());
|
|
}
|
|
string_map.emplace(std::piecewise_construct,
|
|
std::forward_as_tuple(std::move(key).assume_value()),
|
|
std::forward_as_tuple(std::move(val).assume_value()));
|
|
}
|
|
return string_map;
|
|
}
|
|
|
|
utils::result_with_exception_ptr<std::unordered_map<sstring, bytes>> read_string_bytes_map() {
|
|
std::unordered_map<sstring, bytes> map;
|
|
utils::result_with_exception_ptr<uint16_t> n = read_short();
|
|
if (!n) [[unlikely]] {
|
|
return bo::failure(std::move(n).assume_error());
|
|
}
|
|
for (auto i = 0; i < n.assume_value(); i++) {
|
|
utils::result_with_exception_ptr<sstring> key = read_string();
|
|
if (!key) [[unlikely]] {
|
|
return bo::failure(std::move(key).assume_error());
|
|
}
|
|
utils::result_with_exception_ptr<bytes> val = read_bytes();
|
|
if (!val) [[unlikely]] {
|
|
return bo::failure(std::move(val).assume_error());
|
|
}
|
|
map.emplace(std::piecewise_construct,
|
|
std::forward_as_tuple(std::move(key).assume_value()),
|
|
std::forward_as_tuple(std::move(val).assume_value()));
|
|
}
|
|
return map;
|
|
}
|
|
|
|
private:
|
|
enum class options_flag {
|
|
VALUES,
|
|
SKIP_METADATA,
|
|
PAGE_SIZE,
|
|
PAGING_STATE,
|
|
SERIAL_CONSISTENCY,
|
|
TIMESTAMP,
|
|
NAMES_FOR_VALUES
|
|
};
|
|
|
|
using options_flag_enum = super_enum<options_flag,
|
|
options_flag::VALUES,
|
|
options_flag::SKIP_METADATA,
|
|
options_flag::PAGE_SIZE,
|
|
options_flag::PAGING_STATE,
|
|
options_flag::SERIAL_CONSISTENCY,
|
|
options_flag::TIMESTAMP,
|
|
options_flag::NAMES_FOR_VALUES
|
|
>;
|
|
public:
|
|
utils::result_with_exception_ptr<std::unique_ptr<cql3::query_options>> read_options(uint8_t version, const cql3::cql_config& cql_config) {
|
|
utils::result_with_exception_ptr<db::consistency_level> consistency = read_consistency();
|
|
if (!consistency) [[unlikely]] {
|
|
return bo::failure(std::move(consistency).assume_error());
|
|
}
|
|
utils::result_with_exception_ptr<int8_t> b = read_byte();
|
|
if (!b) [[unlikely]] {
|
|
return bo::failure(std::move(b).assume_error());
|
|
}
|
|
auto flags = enum_set<options_flag_enum>::from_mask(b.assume_value());
|
|
std::vector<cql3::raw_value_view> values;
|
|
cql3::unset_bind_variable_vector unset;
|
|
std::vector<std::string_view> names;
|
|
|
|
if (flags.contains<options_flag::VALUES>()) {
|
|
if (flags.contains<options_flag::NAMES_FOR_VALUES>()) {
|
|
utils::result_with_exception_ptr<void> nvl = read_name_and_value_list(version, names, values, unset);
|
|
if (!nvl) [[unlikely]] {
|
|
return bo::failure(std::move(nvl).assume_error());
|
|
}
|
|
} else {
|
|
utils::result_with_exception_ptr<void> vvl = read_value_view_list(version, values, unset);
|
|
if (!vvl) [[unlikely]] {
|
|
return bo::failure(std::move(vvl).assume_error());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool skip_metadata = flags.contains<options_flag::SKIP_METADATA>();
|
|
flags.remove<options_flag::VALUES>();
|
|
flags.remove<options_flag::SKIP_METADATA>();
|
|
|
|
std::unique_ptr<cql3::query_options> options;
|
|
if (flags) {
|
|
lw_shared_ptr<service::pager::paging_state> paging_state;
|
|
int32_t page_size = -1;
|
|
if (flags.contains<options_flag::PAGE_SIZE>()) {
|
|
utils::result_with_exception_ptr<int32_t> v = read_int();
|
|
if (!v) [[unlikely]] {
|
|
return bo::failure(std::move(v).assume_error());
|
|
}
|
|
page_size = v.assume_value();
|
|
}
|
|
if (flags.contains<options_flag::PAGING_STATE>()) {
|
|
utils::result_with_exception_ptr<bytes> bv = read_bytes();
|
|
if (!bv) [[unlikely]] {
|
|
return bo::failure(std::move(bv).assume_error());
|
|
}
|
|
paging_state = service::pager::paging_state::deserialize(bv.assume_value());
|
|
}
|
|
|
|
db::consistency_level serial_consistency = db::consistency_level::SERIAL;
|
|
if (flags.contains<options_flag::SERIAL_CONSISTENCY>()) {
|
|
auto sc = read_consistency();
|
|
if (!sc) [[unlikely]] {
|
|
return bo::failure(std::move(sc).assume_error());
|
|
}
|
|
serial_consistency = sc.assume_value();
|
|
}
|
|
|
|
api::timestamp_type ts = api::missing_timestamp;
|
|
if (flags.contains<options_flag::TIMESTAMP>()) {
|
|
utils::result_with_exception_ptr<int64_t> v = read_long();
|
|
if (!v) [[unlikely]] {
|
|
return bo::failure(std::move(v).assume_error());
|
|
}
|
|
ts = v.assume_value();
|
|
if (ts < api::min_timestamp || ts > api::max_timestamp) {
|
|
return bo::failure(std::make_exception_ptr(exceptions::protocol_exception(format("Out of bound timestamp, must be in [{:d}, {:d}] (got {:d})",
|
|
api::min_timestamp, api::max_timestamp, ts))));
|
|
}
|
|
}
|
|
|
|
std::optional<std::vector<std::string_view>> onames;
|
|
if (!names.empty()) {
|
|
onames = std::move(names);
|
|
}
|
|
options = std::make_unique<cql3::query_options>(cql_config, consistency.assume_value(), std::move(onames),
|
|
cql3::raw_value_view_vector_with_unset(std::move(values), std::move(unset)), skip_metadata,
|
|
cql3::query_options::specific_options{page_size, std::move(paging_state), serial_consistency, ts});
|
|
} else {
|
|
options = std::make_unique<cql3::query_options>(cql_config, consistency.assume_value(), std::nullopt,
|
|
cql3::raw_value_view_vector_with_unset(std::move(values), std::move(unset)), skip_metadata,
|
|
cql3::query_options::specific_options::DEFAULT);
|
|
}
|
|
|
|
return options;
|
|
}
|
|
};
|
|
|
|
}
|