Compare commits

..

10 Commits

Author SHA1 Message Date
Piotr Sarna
fcb349b026 tests: add tests for per-role timeouts
The test cases verify that setting timeout parameters per-role
works and is validated.
2020-11-27 12:43:53 +01:00
Piotr Sarna
28c558af95 docs: add a paragaph about per-role parameters
This paragraph is also the first one in newly crated roles.md,
which should be later filled with more information about roles.
2020-11-27 12:43:53 +01:00
Piotr Sarna
83b47ae394 cql3: add validating per-role timeout options
Per-role timeout options are now validated when set:
 - they should represent a valid duration
 - the duration should have millisecond granularity,
   since the timeout clock does not support micro/nanoseconds.
2020-11-27 12:37:27 +01:00
Piotr Sarna
391d1f2b21 client_state: add updating per-role params
Per-role parameters (currently: read_timeout and write_timeout)
are now updated when a new connection is established.
Also, the changes are immediately propagated for the connection
which sent the CREATE ROLE/ALTER ROLE statement.
The other connections which have the changed role are currently
not immediately reloaded.
It can be done in the future if needed, but all sessions with
given roles should be tracked, or, alternatively, all sessions
should be iterated and changed.
2020-11-27 12:37:27 +01:00
Piotr Sarna
137a8a0161 auth: add options support to password authenticator
Custom options will be used later to provide per-role timeouts
and other useful parameters.
2020-11-27 12:37:17 +01:00
Piotr Sarna
c473cb4a2d treewide: remove timeout config from query options
Timeout config is now stored in each connection, so there's no point
in tracking it inside each query as well. This patch removes
timeout_config from query_options and follows by removing now
unnecessary parameters of many functions and constructors.
2020-11-26 17:56:55 +01:00
Piotr Sarna
98fac66361 cql3: use timeout config from client state instead of query options
... in batch statement, in order to be able to remove the timeout
from query options later.
2020-11-26 17:55:29 +01:00
Piotr Sarna
2cbeb3678f cql3: use timeout config from client state instead of query options
... in modification statement, in order to be able to remove the timeout
from query options later.
2020-11-26 17:55:29 +01:00
Piotr Sarna
d61e1fd174 cql3: use timeout config from client state instead of query options
... in select statement, in order to be able to remove the timeout
from query options later.
2020-11-26 17:55:29 +01:00
Piotr Sarna
f31ac0a8ca service: add timeout config to client state
Future patches will use this per-connection timeout config
to allow setting different timeouts for each session,
based on roles.
2020-11-26 17:55:14 +01:00
1107 changed files with 4552 additions and 14718 deletions

View File

@@ -1,33 +0,0 @@
name: "CI Docs"
on:
push:
branches:
- master
paths:
- 'docs/**'
jobs:
release:
name: Build
runs-on: ubuntu-latest
env:
LATEST_VERSION: master
steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: 3.7
- name: Build docs
run: |
export PATH=$PATH:~/.local/bin
cd docs
make multiversion
- name: Deploy
run : ./docs/_utils/deploy.sh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@@ -25,5 +25,3 @@ tags
testlog
test/*/*.reject
.vscode
docs/_build
docs/poetry.lock

2
.gitmodules vendored
View File

@@ -1,6 +1,6 @@
[submodule "seastar"]
path = seastar
url = ../scylla-seastar
url = ../seastar
ignore = dirty
[submodule "swagger-ui"]
path = swagger-ui

View File

@@ -1,13 +1,11 @@
# Contributing
## Asking questions or requesting help
# Asking questions or requesting help
Use the [ScyllaDB user mailing list](https://groups.google.com/forum/#!forum/scylladb-users) or the [Slack workspace](http://slack.scylladb.com) for general questions and help.
## Reporting an issue
# Reporting an issue
Please use the [Issue Tracker](https://github.com/scylladb/scylla/issues/) to report issues. Fill in as much information as you can in the issue template, especially for performance problems.
## Contributing Code to Scylla
# Contributing Code to Scylla
To contribute code to Scylla, you need to sign the [Contributor License Agreement](https://www.scylladb.com/open-source/contributor-agreement/) and send your changes as [patches](https://github.com/scylladb/scylla/wiki/Formatting-and-sending-patches) to the [mailing list](https://groups.google.com/forum/#!forum/scylladb-dev). We don't accept pull requests on GitHub.

View File

@@ -78,7 +78,10 @@ and the current compatibility of this feature as well as Scylla-specific extensi
## Documentation
Documentation can be found [here](https://scylla.docs.scylladb.com).
Documentation can be found in [./docs](./docs) and on the
[wiki](https://github.com/scylladb/scylla/wiki). There is currently no clear
definition of what goes where, so when looking for something be sure to check
both.
Seastar documentation can be found [here](http://docs.seastar.io/master/index.html).
User documentation can be found [here](https://docs.scylladb.com/).

View File

@@ -1,7 +1,7 @@
#!/bin/sh
PRODUCT=scylla
VERSION=4.4.rc2
VERSION=4.4.dev
if test -f version
then

View File

@@ -129,8 +129,7 @@ future<std::string> get_key_from_roles(cql3::query_processor& qp, std::string us
auth::meta::roles_table::qualified_name, auth::meta::roles_table::role_col_name);
auto cl = auth::password_authenticator::consistency_for_user(username);
auto& timeout = auth::internal_distributed_timeout_config();
return qp.execute_internal(query, cl, timeout, {sstring(username)}, true).then_wrapped([username = std::move(username)] (future<::shared_ptr<cql3::untyped_result_set>> f) {
return qp.execute_internal(query, cl, auth::internal_distributed_query_state(), {sstring(username)}, true).then_wrapped([username = std::move(username)] (future<::shared_ptr<cql3::untyped_result_set>> f) {
auto res = f.get0();
auto salted_hash = std::optional<sstring>();
if (res->empty()) {

View File

@@ -159,40 +159,23 @@ static bool check_NE(const rjson::value* v1, const rjson::value& v2) {
}
// Check if two JSON-encoded values match with the BEGINS_WITH relation
bool check_BEGINS_WITH(const rjson::value* v1, const rjson::value& v2,
bool v1_from_query, bool v2_from_query) {
bool bad = false;
if (!v1 || !v1->IsObject() || v1->MemberCount() != 1) {
if (v1_from_query) {
throw api_error::validation("begins_with() encountered malformed argument");
} else {
bad = true;
}
} else if (v1->MemberBegin()->name != "S" && v1->MemberBegin()->name != "B") {
if (v1_from_query) {
throw api_error::validation(format("begins_with supports only string or binary type, got: {}", *v1));
} else {
bad = true;
}
}
static bool check_BEGINS_WITH(const rjson::value* v1, const rjson::value& v2) {
// BEGINS_WITH requires that its single operand (v2) be a string or
// binary - otherwise it's a validation error. However, problems with
// the stored attribute (v1) will just return false (no match).
if (!v2.IsObject() || v2.MemberCount() != 1) {
if (v2_from_query) {
throw api_error::validation("begins_with() encountered malformed argument");
} else {
bad = true;
}
} else if (v2.MemberBegin()->name != "S" && v2.MemberBegin()->name != "B") {
if (v2_from_query) {
throw api_error::validation(format("begins_with() supports only string or binary type, got: {}", v2));
} else {
bad = true;
}
throw api_error::validation(format("BEGINS_WITH operator encountered malformed AttributeValue: {}", v2));
}
if (bad) {
auto it2 = v2.MemberBegin();
if (it2->name != "S" && it2->name != "B") {
throw api_error::validation(format("BEGINS_WITH operator requires String or Binary type in AttributeValue, got {}", it2->name));
}
if (!v1 || !v1->IsObject() || v1->MemberCount() != 1) {
return false;
}
auto it1 = v1->MemberBegin();
auto it2 = v2.MemberBegin();
if (it1->name != it2->name) {
return false;
}
@@ -296,38 +279,24 @@ static bool check_NOT_NULL(const rjson::value* val) {
return val != nullptr;
}
// Only types S, N or B (string, number or bytes) may be compared by the
// various comparion operators - lt, le, gt, ge, and between.
static bool check_comparable_type(const rjson::value& v) {
if (!v.IsObject() || v.MemberCount() != 1) {
return false;
}
const rjson::value& type = v.MemberBegin()->name;
return type == "S" || type == "N" || type == "B";
}
// Check if two JSON-encoded values match with cmp.
template <typename Comparator>
bool check_compare(const rjson::value* v1, const rjson::value& v2, const Comparator& cmp,
bool v1_from_query, bool v2_from_query) {
bool bad = false;
if (!v1 || !check_comparable_type(*v1)) {
if (v1_from_query) {
throw api_error::validation(format("{} allow only the types String, Number, or Binary", cmp.diagnostic));
}
bad = true;
bool check_compare(const rjson::value* v1, const rjson::value& v2, const Comparator& cmp) {
if (!v2.IsObject() || v2.MemberCount() != 1) {
throw api_error::validation(
format("{} requires a single AttributeValue of type String, Number, or Binary",
cmp.diagnostic));
}
if (!check_comparable_type(v2)) {
if (v2_from_query) {
throw api_error::validation(format("{} allow only the types String, Number, or Binary", cmp.diagnostic));
}
bad = true;
const auto& kv2 = *v2.MemberBegin();
if (kv2.name != "S" && kv2.name != "N" && kv2.name != "B") {
throw api_error::validation(
format("{} requires a single AttributeValue of type String, Number, or Binary",
cmp.diagnostic));
}
if (bad) {
if (!v1 || !v1->IsObject() || v1->MemberCount() != 1) {
return false;
}
const auto& kv1 = *v1->MemberBegin();
const auto& kv2 = *v2.MemberBegin();
if (kv1.name != kv2.name) {
return false;
}
@@ -341,8 +310,7 @@ bool check_compare(const rjson::value* v1, const rjson::value& v2, const Compara
if (kv1.name == "B") {
return cmp(base64_decode(kv1.value), base64_decode(kv2.value));
}
// cannot reach here, as check_comparable_type() verifies the type is one
// of the above options.
clogger.error("check_compare panic: LHS type equals RHS type, but one is in {N,S,B} while the other isn't");
return false;
}
@@ -373,71 +341,56 @@ struct cmp_gt {
static constexpr const char* diagnostic = "GT operator";
};
// True if v is between lb and ub, inclusive. Throws or returns false
// (depending on bounds_from_query parameter) if lb > ub.
// True if v is between lb and ub, inclusive. Throws if lb > ub.
template <typename T>
static bool check_BETWEEN(const T& v, const T& lb, const T& ub, bool bounds_from_query) {
static bool check_BETWEEN(const T& v, const T& lb, const T& ub) {
if (cmp_lt()(ub, lb)) {
if (bounds_from_query) {
throw api_error::validation(
format("BETWEEN operator requires lower_bound <= upper_bound, but {} > {}", lb, ub));
} else {
return false;
}
throw api_error::validation(
format("BETWEEN operator requires lower_bound <= upper_bound, but {} > {}", lb, ub));
}
return cmp_ge()(v, lb) && cmp_le()(v, ub);
}
static bool check_BETWEEN(const rjson::value* v, const rjson::value& lb, const rjson::value& ub,
bool v_from_query, bool lb_from_query, bool ub_from_query) {
if ((v && v_from_query && !check_comparable_type(*v)) ||
(lb_from_query && !check_comparable_type(lb)) ||
(ub_from_query && !check_comparable_type(ub))) {
throw api_error::validation("between allow only the types String, Number, or Binary");
}
if (!v || !v->IsObject() || v->MemberCount() != 1 ||
!lb.IsObject() || lb.MemberCount() != 1 ||
!ub.IsObject() || ub.MemberCount() != 1) {
static bool check_BETWEEN(const rjson::value* v, const rjson::value& lb, const rjson::value& ub) {
if (!v) {
return false;
}
if (!v->IsObject() || v->MemberCount() != 1) {
throw api_error::validation(format("BETWEEN operator encountered malformed AttributeValue: {}", *v));
}
if (!lb.IsObject() || lb.MemberCount() != 1) {
throw api_error::validation(format("BETWEEN operator encountered malformed AttributeValue: {}", lb));
}
if (!ub.IsObject() || ub.MemberCount() != 1) {
throw api_error::validation(format("BETWEEN operator encountered malformed AttributeValue: {}", ub));
}
const auto& kv_v = *v->MemberBegin();
const auto& kv_lb = *lb.MemberBegin();
const auto& kv_ub = *ub.MemberBegin();
bool bounds_from_query = lb_from_query && ub_from_query;
if (kv_lb.name != kv_ub.name) {
if (bounds_from_query) {
throw api_error::validation(
throw api_error::validation(
format("BETWEEN operator requires the same type for lower and upper bound; instead got {} and {}",
kv_lb.name, kv_ub.name));
} else {
return false;
}
}
if (kv_v.name != kv_lb.name) { // Cannot compare different types, so v is NOT between lb and ub.
return false;
}
if (kv_v.name == "N") {
const char* diag = "BETWEEN operator";
return check_BETWEEN(unwrap_number(*v, diag), unwrap_number(lb, diag), unwrap_number(ub, diag), bounds_from_query);
return check_BETWEEN(unwrap_number(*v, diag), unwrap_number(lb, diag), unwrap_number(ub, diag));
}
if (kv_v.name == "S") {
return check_BETWEEN(std::string_view(kv_v.value.GetString(), kv_v.value.GetStringLength()),
std::string_view(kv_lb.value.GetString(), kv_lb.value.GetStringLength()),
std::string_view(kv_ub.value.GetString(), kv_ub.value.GetStringLength()),
bounds_from_query);
std::string_view(kv_ub.value.GetString(), kv_ub.value.GetStringLength()));
}
if (kv_v.name == "B") {
return check_BETWEEN(base64_decode(kv_v.value), base64_decode(kv_lb.value), base64_decode(kv_ub.value), bounds_from_query);
return check_BETWEEN(base64_decode(kv_v.value), base64_decode(kv_lb.value), base64_decode(kv_ub.value));
}
if (v_from_query) {
throw api_error::validation(
format("BETWEEN operator requires AttributeValueList elements to be of type String, Number, or Binary; instead got {}",
throw api_error::validation(
format("BETWEEN operator requires AttributeValueList elements to be of type String, Number, or Binary; instead got {}",
kv_lb.name));
} else {
return false;
}
}
// Verify one Expect condition on one attribute (whose content is "got")
@@ -484,19 +437,19 @@ static bool verify_expected_one(const rjson::value& condition, const rjson::valu
return check_NE(got, (*attribute_value_list)[0]);
case comparison_operator_type::LT:
verify_operand_count(attribute_value_list, exact_size(1), *comparison_operator);
return check_compare(got, (*attribute_value_list)[0], cmp_lt{}, false, true);
return check_compare(got, (*attribute_value_list)[0], cmp_lt{});
case comparison_operator_type::LE:
verify_operand_count(attribute_value_list, exact_size(1), *comparison_operator);
return check_compare(got, (*attribute_value_list)[0], cmp_le{}, false, true);
return check_compare(got, (*attribute_value_list)[0], cmp_le{});
case comparison_operator_type::GT:
verify_operand_count(attribute_value_list, exact_size(1), *comparison_operator);
return check_compare(got, (*attribute_value_list)[0], cmp_gt{}, false, true);
return check_compare(got, (*attribute_value_list)[0], cmp_gt{});
case comparison_operator_type::GE:
verify_operand_count(attribute_value_list, exact_size(1), *comparison_operator);
return check_compare(got, (*attribute_value_list)[0], cmp_ge{}, false, true);
return check_compare(got, (*attribute_value_list)[0], cmp_ge{});
case comparison_operator_type::BEGINS_WITH:
verify_operand_count(attribute_value_list, exact_size(1), *comparison_operator);
return check_BEGINS_WITH(got, (*attribute_value_list)[0], false, true);
return check_BEGINS_WITH(got, (*attribute_value_list)[0]);
case comparison_operator_type::IN:
verify_operand_count(attribute_value_list, nonempty(), *comparison_operator);
return check_IN(got, *attribute_value_list);
@@ -508,8 +461,7 @@ static bool verify_expected_one(const rjson::value& condition, const rjson::valu
return check_NOT_NULL(got);
case comparison_operator_type::BETWEEN:
verify_operand_count(attribute_value_list, exact_size(2), *comparison_operator);
return check_BETWEEN(got, (*attribute_value_list)[0], (*attribute_value_list)[1],
false, true, true);
return check_BETWEEN(got, (*attribute_value_list)[0], (*attribute_value_list)[1]);
case comparison_operator_type::CONTAINS:
{
verify_operand_count(attribute_value_list, exact_size(1), *comparison_operator);
@@ -621,8 +573,7 @@ static bool calculate_primitive_condition(const parsed::primitive_condition& con
// Shouldn't happen unless we have a bug in the parser
throw std::logic_error(format("Wrong number of values {} in BETWEEN primitive_condition", cond._values.size()));
}
return check_BETWEEN(&calculated_values[0], calculated_values[1], calculated_values[2],
cond._values[0].is_constant(), cond._values[1].is_constant(), cond._values[2].is_constant());
return check_BETWEEN(&calculated_values[0], calculated_values[1], calculated_values[2]);
case parsed::primitive_condition::type::IN:
return check_IN(calculated_values);
case parsed::primitive_condition::type::VALUE:
@@ -653,17 +604,13 @@ static bool calculate_primitive_condition(const parsed::primitive_condition& con
case parsed::primitive_condition::type::NE:
return check_NE(&calculated_values[0], calculated_values[1]);
case parsed::primitive_condition::type::GT:
return check_compare(&calculated_values[0], calculated_values[1], cmp_gt{},
cond._values[0].is_constant(), cond._values[1].is_constant());
return check_compare(&calculated_values[0], calculated_values[1], cmp_gt{});
case parsed::primitive_condition::type::GE:
return check_compare(&calculated_values[0], calculated_values[1], cmp_ge{},
cond._values[0].is_constant(), cond._values[1].is_constant());
return check_compare(&calculated_values[0], calculated_values[1], cmp_ge{});
case parsed::primitive_condition::type::LT:
return check_compare(&calculated_values[0], calculated_values[1], cmp_lt{},
cond._values[0].is_constant(), cond._values[1].is_constant());
return check_compare(&calculated_values[0], calculated_values[1], cmp_lt{});
case parsed::primitive_condition::type::LE:
return check_compare(&calculated_values[0], calculated_values[1], cmp_le{},
cond._values[0].is_constant(), cond._values[1].is_constant());
return check_compare(&calculated_values[0], calculated_values[1], cmp_le{});
default:
// Shouldn't happen unless we have a bug in the parser
throw std::logic_error(format("Unknown type {} in primitive_condition object", (int)(cond._op)));

View File

@@ -52,7 +52,6 @@ bool verify_expected(const rjson::value& req, const rjson::value* previous_item)
bool verify_condition(const rjson::value& condition, bool require_all, const rjson::value* previous_item);
bool check_CONTAINS(const rjson::value* v1, const rjson::value& v2);
bool check_BEGINS_WITH(const rjson::value* v1, const rjson::value& v2, bool v1_from_query, bool v2_from_query);
bool verify_condition_expression(
const parsed::condition_expression& condition_expression,

View File

@@ -59,9 +59,6 @@ public:
static api_error invalid_signature(std::string msg) {
return api_error("InvalidSignatureException", std::move(msg));
}
static api_error missing_authentication_token(std::string msg) {
return api_error("MissingAuthenticationTokenException", std::move(msg));
}
static api_error unrecognized_client(std::string msg) {
return api_error("UnrecognizedClientException", std::move(msg));
}

View File

@@ -55,7 +55,7 @@
#include "schema.hh"
#include "alternator/tags_extension.hh"
#include "alternator/rmw_operation.hh"
#include <seastar/core/coroutine.hh>
#include <boost/range/adaptors.hpp>
logging::logger elogger("alternator-executor");
@@ -220,7 +220,7 @@ static std::tuple<bool, std::string_view, std::string_view> try_get_internal_tab
std::string_view ks_name = table_name.substr(0, delim);
table_name.remove_prefix(ks_name.size() + 1);
// Only internal keyspaces can be accessed to avoid leakage
if (!is_internal_keyspace(ks_name)) {
if (!is_internal_keyspace(sstring(ks_name))) {
return {false, "", ""};
}
return {true, ks_name, table_name};
@@ -476,8 +476,8 @@ future<executor::request_return_type> executor::delete_table(client_state& clien
return make_ready_future<request_return_type>(api_error::resource_not_found(
format("Requested resource not found: Table: {} not found", table_name)));
}
return _mm.announce_column_family_drop(keyspace_name, table_name, service::migration_manager::drop_views::yes).then([this, keyspace_name] {
return _mm.announce_keyspace_drop(keyspace_name);
return _mm.announce_column_family_drop(keyspace_name, table_name, false, service::migration_manager::drop_views::yes).then([this, keyspace_name] {
return _mm.announce_keyspace_drop(keyspace_name, false);
}).then([table_name = std::move(table_name)] {
// FIXME: need more attributes?
rjson::value table_description = rjson::empty_object();
@@ -704,48 +704,52 @@ static void update_tags_map(const rjson::value& tags, std::map<sstring, sstring>
static future<> update_tags(service::migration_manager& mm, schema_ptr schema, std::map<sstring, sstring>&& tags_map) {
schema_builder builder(schema);
builder.add_extension(tags_extension::NAME, ::make_shared<tags_extension>(std::move(tags_map)));
return mm.announce_column_family_update(builder.build(), false, std::vector<view_ptr>());
return mm.announce_column_family_update(builder.build(), false, std::vector<view_ptr>(), false);
}
future<executor::request_return_type> executor::tag_resource(client_state& client_state, service_permit permit, rjson::value request) {
_stats.api_operations.tag_resource++;
const rjson::value* arn = rjson::find(request, "ResourceArn");
if (!arn || !arn->IsString()) {
co_return api_error::access_denied("Incorrect resource identifier");
}
schema_ptr schema = get_table_from_arn(_proxy, rjson::to_string_view(*arn));
std::map<sstring, sstring> tags_map = get_tags_of_table(schema);
const rjson::value* tags = rjson::find(request, "Tags");
if (!tags || !tags->IsArray()) {
co_return api_error::validation("Cannot parse tags");
}
if (tags->Size() < 1) {
co_return api_error::validation("The number of tags must be at least 1") ;
}
update_tags_map(*tags, tags_map, update_tags_action::add_tags);
co_await update_tags(_mm, schema, std::move(tags_map));
co_return json_string("");
return seastar::async([this, &client_state, request = std::move(request)] () mutable -> request_return_type {
const rjson::value* arn = rjson::find(request, "ResourceArn");
if (!arn || !arn->IsString()) {
return api_error::access_denied("Incorrect resource identifier");
}
schema_ptr schema = get_table_from_arn(_proxy, rjson::to_string_view(*arn));
std::map<sstring, sstring> tags_map = get_tags_of_table(schema);
const rjson::value* tags = rjson::find(request, "Tags");
if (!tags || !tags->IsArray()) {
return api_error::validation("Cannot parse tags");
}
if (tags->Size() < 1) {
return api_error::validation("The number of tags must be at least 1") ;
}
update_tags_map(*tags, tags_map, update_tags_action::add_tags);
update_tags(_mm, schema, std::move(tags_map)).get();
return json_string("");
});
}
future<executor::request_return_type> executor::untag_resource(client_state& client_state, service_permit permit, rjson::value request) {
_stats.api_operations.untag_resource++;
const rjson::value* arn = rjson::find(request, "ResourceArn");
if (!arn || !arn->IsString()) {
co_return api_error::access_denied("Incorrect resource identifier");
}
const rjson::value* tags = rjson::find(request, "TagKeys");
if (!tags || !tags->IsArray()) {
co_return api_error::validation(format("Cannot parse tag keys"));
}
return seastar::async([this, &client_state, request = std::move(request)] () -> request_return_type {
const rjson::value* arn = rjson::find(request, "ResourceArn");
if (!arn || !arn->IsString()) {
return api_error::access_denied("Incorrect resource identifier");
}
const rjson::value* tags = rjson::find(request, "TagKeys");
if (!tags || !tags->IsArray()) {
return api_error::validation(format("Cannot parse tag keys"));
}
schema_ptr schema = get_table_from_arn(_proxy, rjson::to_string_view(*arn));
schema_ptr schema = get_table_from_arn(_proxy, rjson::to_string_view(*arn));
std::map<sstring, sstring> tags_map = get_tags_of_table(schema);
update_tags_map(*tags, tags_map, update_tags_action::delete_tags);
co_await update_tags(_mm, schema, std::move(tags_map));
co_return json_string("");
std::map<sstring, sstring> tags_map = get_tags_of_table(schema);
update_tags_map(*tags, tags_map, update_tags_action::delete_tags);
update_tags(_mm, schema, std::move(tags_map)).get();
return json_string("");
});
}
future<executor::request_return_type> executor::list_tags_of_resource(client_state& client_state, service_permit permit, rjson::value request) {
@@ -981,7 +985,7 @@ future<executor::request_return_type> executor::create_table(client_state& clien
return create_keyspace(keyspace_name).handle_exception_type([] (exceptions::already_exists_exception&) {
// Ignore the fact that the keyspace may already exist. See discussion in #6340
}).then([this, table_name, request = std::move(request), schema, view_builders = std::move(view_builders), tags_map = std::move(tags_map)] () mutable {
return futurize_invoke([&] { return _mm.announce_new_column_family(schema); }).then([this, table_info = std::move(request), schema, view_builders = std::move(view_builders), tags_map = std::move(tags_map)] () mutable {
return futurize_invoke([&] { return _mm.announce_new_column_family(schema, false); }).then([this, table_info = std::move(request), schema, view_builders = std::move(view_builders), tags_map = std::move(tags_map)] () mutable {
return parallel_for_each(std::move(view_builders), [this, schema] (schema_builder builder) {
return _mm.announce_new_view(view_ptr(builder.build()));
}).then([this, table_info = std::move(table_info), schema, tags_map = std::move(tags_map)] () mutable {
@@ -1237,16 +1241,10 @@ mutation put_or_delete_item::build(schema_ptr schema, api::timestamp_type ts) co
return m;
}
// The DynamoDB API doesn't let the client control the server's timeout, so
// we have a global default_timeout() for Alternator requests. The value of
// default_timeout is overwritten by main.cc based on the
// "alternator_timeout_in_ms" configuration parameter.
db::timeout_clock::duration executor::s_default_timeout = 10s;
void executor::set_default_timeout(db::timeout_clock::duration timeout) {
s_default_timeout = timeout;
}
// The DynamoDB API doesn't let the client control the server's timeout.
// Let's pick something reasonable:
db::timeout_clock::time_point executor::default_timeout() {
return db::timeout_clock::now() + s_default_timeout;
return db::timeout_clock::now() + 10s;
}
static future<std::unique_ptr<rjson::value>> get_previous_item(
@@ -2247,30 +2245,19 @@ update_item_operation::apply(std::unique_ptr<rjson::value> previous_item, api::t
rjson::value v1 = calculate_value(base, calculate_value_caller::UpdateExpression, previous_item.get());
rjson::value v2 = calculate_value(addition, calculate_value_caller::UpdateExpression, previous_item.get());
rjson::value result;
// An ADD can be used to create a new attribute (when
// v1.IsNull()) or to add to a pre-existing attribute:
if (v1.IsNull()) {
std::string v2_type = get_item_type_string(v2);
if (v2_type == "N" || v2_type == "SS" || v2_type == "NS" || v2_type == "BS") {
result = v2;
} else {
throw api_error::validation(format("An operand in the update expression has an incorrect data type: {}", v2));
std::string v1_type = get_item_type_string(v1);
if (v1_type == "N") {
if (get_item_type_string(v2) != "N") {
throw api_error::validation(format("Incorrect operand type for operator or function. Expected {}: {}", v1_type, rjson::print(v2)));
}
result = number_add(v1, v2);
} else if (v1_type == "SS" || v1_type == "NS" || v1_type == "BS") {
if (get_item_type_string(v2) != v1_type) {
throw api_error::validation(format("Incorrect operand type for operator or function. Expected {}: {}", v1_type, rjson::print(v2)));
}
result = set_sum(v1, v2);
} else {
std::string v1_type = get_item_type_string(v1);
if (v1_type == "N") {
if (get_item_type_string(v2) != "N") {
throw api_error::validation(format("Incorrect operand type for operator or function. Expected {}: {}", v1_type, rjson::print(v2)));
}
result = number_add(v1, v2);
} else if (v1_type == "SS" || v1_type == "NS" || v1_type == "BS") {
if (get_item_type_string(v2) != v1_type) {
throw api_error::validation(format("Incorrect operand type for operator or function. Expected {}: {}", v1_type, rjson::print(v2)));
}
result = set_sum(v1, v2);
} else {
throw api_error::validation(format("An operand in the update expression has an incorrect data type: {}", v1));
}
throw api_error::validation(format("An operand in the update expression has an incorrect data type: {}", v1));
}
do_update(to_bytes(column_name), result);
},
@@ -2801,7 +2788,7 @@ static rjson::value encode_paging_state(const schema& schema, const service::pag
for (const column_definition& cdef : schema.partition_key_columns()) {
rjson::set_with_string_name(last_evaluated_key, std::string_view(cdef.name_as_text()), rjson::empty_object());
rjson::value& key_entry = last_evaluated_key[cdef.name_as_text()];
rjson::set_with_string_name(key_entry, type_to_string(cdef.type), json_key_column_value(*exploded_pk_it, cdef));
rjson::set_with_string_name(key_entry, type_to_string(cdef.type), rjson::parse(to_json_string(*cdef.type, *exploded_pk_it)));
++exploded_pk_it;
}
auto ck = paging_state.get_clustering_key();
@@ -2811,7 +2798,7 @@ static rjson::value encode_paging_state(const schema& schema, const service::pag
for (const column_definition& cdef : schema.clustering_key_columns()) {
rjson::set_with_string_name(last_evaluated_key, std::string_view(cdef.name_as_text()), rjson::empty_object());
rjson::value& key_entry = last_evaluated_key[cdef.name_as_text()];
rjson::set_with_string_name(key_entry, type_to_string(cdef.type), json_key_column_value(*exploded_ck_it, cdef));
rjson::set_with_string_name(key_entry, type_to_string(cdef.type), rjson::parse(to_json_string(*cdef.type, *exploded_ck_it)));
++exploded_ck_it;
}
}
@@ -2858,12 +2845,12 @@ static future<executor::request_return_type> do_query(service::storage_proxy& pr
auto query_state_ptr = std::make_unique<service::query_state>(client_state, trace_state, std::move(permit));
command->slice.options.set<query::partition_slice::option::allow_short_read>();
auto query_options = std::make_unique<cql3::query_options>(cl, infinite_timeout_config, std::vector<cql3::raw_value>{});
auto query_options = std::make_unique<cql3::query_options>(cl, std::vector<cql3::raw_value>{});
query_options = std::make_unique<cql3::query_options>(std::move(query_options), std::move(paging_state));
auto p = service::pager::query_pagers::pager(schema, selection, *query_state_ptr, *query_options, command, std::move(partition_ranges), nullptr);
return p->fetch_page(limit, gc_clock::now(), executor::default_timeout()).then(
[p = std::move(p), schema, cql_stats, partition_slice = std::move(partition_slice),
[p, schema, cql_stats, partition_slice = std::move(partition_slice),
selection = std::move(selection), query_state_ptr = std::move(query_state_ptr),
attrs_to_get = std::move(attrs_to_get),
query_options = std::move(query_options),
@@ -3549,7 +3536,7 @@ future<> executor::create_keyspace(std::string_view keyspace_name) {
}
auto opts = get_network_topology_options(rf);
auto ksm = keyspace_metadata::new_keyspace(keyspace_name_str, "org.apache.cassandra.locator.NetworkTopologyStrategy", std::move(opts), true);
return _mm.announce_new_keyspace(ksm, api::new_timestamp());
return _mm.announce_new_keyspace(ksm, api::new_timestamp(), false);
});
}

View File

@@ -121,10 +121,6 @@ public:
static sstring table_name(const schema&);
static db::timeout_clock::time_point default_timeout();
static void set_default_timeout(db::timeout_clock::duration timeout);
private:
static db::timeout_clock::duration s_default_timeout;
public:
static schema_ptr find_table(service::storage_proxy&, const rjson::value& request);
private:

View File

@@ -603,8 +603,52 @@ std::unordered_map<std::string_view, function_handler_type*> function_handlers {
}
rjson::value v1 = calculate_value(f._parameters[0], caller, previous_item);
rjson::value v2 = calculate_value(f._parameters[1], caller, previous_item);
return to_bool_json(check_BEGINS_WITH(v1.IsNull() ? nullptr : &v1, v2,
f._parameters[0].is_constant(), f._parameters[1].is_constant()));
// TODO: There's duplication here with check_BEGINS_WITH().
// But unfortunately, the two functions differ a bit.
// If one of v1 or v2 is malformed or has an unsupported type
// (not B or S), what we do depends on whether it came from
// the user's query (is_constant()), or the item. Unsupported
// values in the query result in an error, but if they are in
// the item, we silently return false (no match).
bool bad = false;
if (!v1.IsObject() || v1.MemberCount() != 1) {
bad = true;
if (f._parameters[0].is_constant()) {
throw api_error::validation(format("{}: begins_with() encountered malformed AttributeValue: {}", caller, v1));
}
} else if (v1.MemberBegin()->name != "S" && v1.MemberBegin()->name != "B") {
bad = true;
if (f._parameters[0].is_constant()) {
throw api_error::validation(format("{}: begins_with() supports only string or binary in AttributeValue: {}", caller, v1));
}
}
if (!v2.IsObject() || v2.MemberCount() != 1) {
bad = true;
if (f._parameters[1].is_constant()) {
throw api_error::validation(format("{}: begins_with() encountered malformed AttributeValue: {}", caller, v2));
}
} else if (v2.MemberBegin()->name != "S" && v2.MemberBegin()->name != "B") {
bad = true;
if (f._parameters[1].is_constant()) {
throw api_error::validation(format("{}: begins_with() supports only string or binary in AttributeValue: {}", caller, v2));
}
}
bool ret = false;
if (!bad) {
auto it1 = v1.MemberBegin();
auto it2 = v2.MemberBegin();
if (it1->name == it2->name) {
if (it2->name == "S") {
std::string_view val1 = rjson::to_string_view(it1->value);
std::string_view val2 = rjson::to_string_view(it2->value);
ret = val1.starts_with(val2);
} else /* it2->name == "B" */ {
ret = base64_begins_with(rjson::to_string_view(it1->value), rjson::to_string_view(it2->value));
}
}
}
return to_bool_json(ret);
}
},
{"contains", [] (calculate_value_caller caller, const rjson::value* previous_item, const parsed::value::function_call& f) {

View File

@@ -189,7 +189,7 @@ future<> server::verify_signature(const request& req) {
}
auto authorization_it = req._headers.find("Authorization");
if (authorization_it == req._headers.end()) {
throw api_error::missing_authentication_token("Authorization header is mandatory for signature verification");
throw api_error::invalid_signature("Authorization header is mandatory for signature verification");
}
std::string host = host_it->second;
std::vector<std::string_view> credentials_raw = split(authorization_it->second, ' ');

View File

@@ -2925,10 +2925,6 @@
"id":"toppartitions_query_results",
"description":"nodetool toppartitions query results",
"properties":{
"read_cardinality":{
"type":"long",
"description":"Number of the unique operations in the sample set"
},
"read":{
"type":"array",
"items":{
@@ -2936,10 +2932,6 @@
},
"description":"Read results"
},
"write_cardinality":{
"type":"long",
"description":"Number of the unique operations in the sample set"
},
"write":{
"type":"array",
"items":{

View File

@@ -148,30 +148,6 @@
]
}
]
},
{
"path":"/gossiper/force_remove_endpoint/{addr}",
"operations":[
{
"method":"POST",
"summary":"Force remove an endpoint from gossip",
"type":"void",
"nickname":"force_remove_endpoint",
"produces":[
"application/json"
],
"parameters":[
{
"name":"addr",
"description":"The endpoint address",
"required":true,
"allowMultiple":false,
"type":"string",
"paramType":"path"
}
]
}
]
}
]
}

View File

@@ -310,7 +310,7 @@ void set_column_family(http_context& ctx, routes& r) {
return res;
});
cf::get_column_family.set(r, [&ctx] (std::unique_ptr<request> req){
cf::get_column_family.set(r, [&ctx] (const_req req){
vector<cf::column_family_info> res;
for (auto i: ctx.db.local().get_column_families_mapping()) {
cf::column_family_info info;
@@ -319,7 +319,7 @@ void set_column_family(http_context& ctx, routes& r) {
info.type = "ColumnFamilies";
res.push_back(info);
}
return make_ready_future<json::json_return_type>(json::stream_object(std::move(res)));
return res;
});
cf::get_column_family_name_keyspace.set(r, [&ctx] (const_req req){
@@ -656,7 +656,7 @@ void set_column_family(http_context& ctx, routes& r) {
cf::get_bloom_filter_disk_space_used.set(r, [&ctx] (std::unique_ptr<request> req) {
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (column_family& cf) {
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
return s + sst->filter_size();
return sst->filter_size();
});
}, std::plus<uint64_t>());
});
@@ -664,7 +664,7 @@ void set_column_family(http_context& ctx, routes& r) {
cf::get_all_bloom_filter_disk_space_used.set(r, [&ctx] (std::unique_ptr<request> req) {
return map_reduce_cf(ctx, uint64_t(0), [] (column_family& cf) {
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
return s + sst->filter_size();
return sst->filter_size();
});
}, std::plus<uint64_t>());
});
@@ -672,7 +672,7 @@ void set_column_family(http_context& ctx, routes& r) {
cf::get_bloom_filter_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<request> req) {
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (column_family& cf) {
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
return s + sst->filter_memory_size();
return sst->filter_memory_size();
});
}, std::plus<uint64_t>());
});
@@ -680,7 +680,7 @@ void set_column_family(http_context& ctx, routes& r) {
cf::get_all_bloom_filter_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<request> req) {
return map_reduce_cf(ctx, uint64_t(0), [] (column_family& cf) {
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
return s + sst->filter_memory_size();
return sst->filter_memory_size();
});
}, std::plus<uint64_t>());
});
@@ -688,7 +688,7 @@ void set_column_family(http_context& ctx, routes& r) {
cf::get_index_summary_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<request> req) {
return map_reduce_cf(ctx, req->param["name"], uint64_t(0), [] (column_family& cf) {
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
return s + sst->get_summary().memory_footprint();
return sst->get_summary().memory_footprint();
});
}, std::plus<uint64_t>());
});
@@ -696,7 +696,7 @@ void set_column_family(http_context& ctx, routes& r) {
cf::get_all_index_summary_off_heap_memory_used.set(r, [&ctx] (std::unique_ptr<request> req) {
return map_reduce_cf(ctx, uint64_t(0), [] (column_family& cf) {
return std::accumulate(cf.get_sstables()->begin(), cf.get_sstables()->end(), uint64_t(0), [](uint64_t s, auto& sst) {
return s + sst->get_summary().memory_footprint();
return sst->get_summary().memory_footprint();
});
}, std::plus<uint64_t>());
});
@@ -991,9 +991,6 @@ void set_column_family(http_context& ctx, routes& r) {
apilog.debug("toppartitions query: processing results");
cf::toppartitions_query_results results;
results.read_cardinality = topk_results.read.size();
results.write_cardinality = topk_results.write.size();
for (auto& d: topk_results.read.top(q.list_size())) {
cf::toppartitions_record r;
r.partition = sstring(d.item);

View File

@@ -66,13 +66,6 @@ void set_gossiper(http_context& ctx, routes& r) {
return make_ready_future<json::json_return_type>(json_void());
});
});
httpd::gossiper_json::force_remove_endpoint.set(r, [](std::unique_ptr<request> req) {
gms::inet_address ep(req->param["addr"]);
return gms::get_local_gossiper().force_remove_endpoint(ep).then([] {
return make_ready_future<json::json_return_type>(json_void());
});
});
}
}

View File

@@ -82,7 +82,7 @@ static future<> create_metadata_table_if_missing_impl(
b.set_uuid(uuid);
schema_ptr table = b.build();
return ignore_existing([&mm, table = std::move(table)] () {
return mm.announce_new_column_family(table);
return mm.announce_new_column_family(table, false);
});
}
@@ -108,7 +108,7 @@ future<> wait_for_schema_agreement(::service::migration_manager& mm, const datab
});
}
const timeout_config& internal_distributed_timeout_config() noexcept {
::service::query_state& internal_distributed_query_state() noexcept {
#ifdef DEBUG
// Give the much slower debug tests more headroom for completing auth queries.
static const auto t = 30s;
@@ -116,7 +116,9 @@ const timeout_config& internal_distributed_timeout_config() noexcept {
static const auto t = 5s;
#endif
static const timeout_config tc{t, t, t, t, t, t, t};
return tc;
static thread_local ::service::client_state cs(::service::client_state::internal_tag{}, tc);
static thread_local ::service::query_state qs(cs, empty_service_permit());
return qs;
}
}

View File

@@ -35,6 +35,7 @@
#include "log.hh"
#include "seastarx.hh"
#include "utils/exponential_backoff_retry.hh"
#include "service/query_state.hh"
using namespace std::chrono_literals;
@@ -87,6 +88,6 @@ future<> wait_for_schema_agreement(::service::migration_manager&, const database
///
/// Time-outs for internal, non-local CQL queries.
///
const timeout_config& internal_distributed_timeout_config() noexcept;
::service::query_state& internal_distributed_query_state() noexcept;
}

View File

@@ -103,7 +103,6 @@ future<bool> default_authorizer::any_granted() const {
return _qp.execute_internal(
query,
db::consistency_level::LOCAL_ONE,
infinite_timeout_config,
{},
true).then([this](::shared_ptr<cql3::untyped_result_set> results) {
return !results->empty();
@@ -116,8 +115,7 @@ future<> default_authorizer::migrate_legacy_metadata() const {
return _qp.execute_internal(
query,
db::consistency_level::LOCAL_ONE,
infinite_timeout_config).then([this](::shared_ptr<cql3::untyped_result_set> results) {
db::consistency_level::LOCAL_ONE).then([this](::shared_ptr<cql3::untyped_result_set> results) {
return do_for_each(*results, [this](const cql3::untyped_result_set_row& row) {
return do_with(
row.get_as<sstring>("username"),
@@ -197,7 +195,6 @@ default_authorizer::authorize(const role_or_anonymous& maybe_role, const resourc
return _qp.execute_internal(
query,
db::consistency_level::LOCAL_ONE,
infinite_timeout_config,
{*maybe_role.name, r.name()}).then([](::shared_ptr<cql3::untyped_result_set> results) {
if (results->empty()) {
return permissions::NONE;
@@ -226,7 +223,7 @@ default_authorizer::modify(
return _qp.execute_internal(
query,
db::consistency_level::ONE,
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{permissions::to_strings(set), sstring(role_name), resource.name()}).discard_result();
});
}
@@ -251,7 +248,7 @@ future<std::vector<permission_details>> default_authorizer::list_all() const {
return _qp.execute_internal(
query,
db::consistency_level::ONE,
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{},
true).then([](::shared_ptr<cql3::untyped_result_set> results) {
std::vector<permission_details> all_details;
@@ -278,7 +275,7 @@ future<> default_authorizer::revoke_all(std::string_view role_name) const {
return _qp.execute_internal(
query,
db::consistency_level::ONE,
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(role_name)}).discard_result().handle_exception([role_name](auto ep) {
try {
std::rethrow_exception(ep);
@@ -298,7 +295,6 @@ future<> default_authorizer::revoke_all(const resource& resource) const {
return _qp.execute_internal(
query,
db::consistency_level::LOCAL_ONE,
infinite_timeout_config,
{resource.name()}).then_wrapped([this, resource](future<::shared_ptr<cql3::untyped_result_set>> f) {
try {
auto res = f.get0();
@@ -315,7 +311,6 @@ future<> default_authorizer::revoke_all(const resource& resource) const {
return _qp.execute_internal(
query,
db::consistency_level::LOCAL_ONE,
infinite_timeout_config,
{r.get_as<sstring>(ROLE_NAME), resource.name()}).discard_result().handle_exception(
[resource](auto ep) {
try {

View File

@@ -66,6 +66,7 @@ constexpr std::string_view password_authenticator_name("org.apache.cassandra.aut
// name of the hash column.
static constexpr std::string_view SALTED_HASH = "salted_hash";
static constexpr std::string_view OPTIONS = "options";
static constexpr std::string_view DEFAULT_USER_NAME = meta::DEFAULT_SUPERUSER_NAME;
static const sstring DEFAULT_USER_PASSWORD = sstring(meta::DEFAULT_SUPERUSER_NAME);
@@ -114,7 +115,7 @@ future<> password_authenticator::migrate_legacy_metadata() const {
return _qp.execute_internal(
query,
db::consistency_level::QUORUM,
internal_distributed_timeout_config()).then([this](::shared_ptr<cql3::untyped_result_set> results) {
internal_distributed_query_state()).then([this](::shared_ptr<cql3::untyped_result_set> results) {
return do_for_each(*results, [this](const cql3::untyped_result_set_row& row) {
auto username = row.get_as<sstring>("username");
auto salted_hash = row.get_as<sstring>(SALTED_HASH);
@@ -122,7 +123,7 @@ future<> password_authenticator::migrate_legacy_metadata() const {
return _qp.execute_internal(
update_row_query(),
consistency_for_user(username),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{std::move(salted_hash), username}).discard_result();
}).finally([results] {});
}).then([] {
@@ -139,7 +140,7 @@ future<> password_authenticator::create_default_if_missing() const {
return _qp.execute_internal(
update_row_query(),
db::consistency_level::QUORUM,
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{passwords::hash(DEFAULT_USER_PASSWORD, rng_for_salt), DEFAULT_USER_NAME}).then([](auto&&) {
plogger.info("Created default superuser authentication record.");
});
@@ -203,11 +204,11 @@ bool password_authenticator::require_authentication() const {
}
authentication_option_set password_authenticator::supported_options() const {
return authentication_option_set{authentication_option::password};
return authentication_option_set{authentication_option::password, authentication_option::options};
}
authentication_option_set password_authenticator::alterable_options() const {
return authentication_option_set{authentication_option::password};
return authentication_option_set{authentication_option::password, authentication_option::options};
}
future<authenticated_user> password_authenticator::authenticate(
@@ -236,7 +237,7 @@ future<authenticated_user> password_authenticator::authenticate(
return _qp.execute_internal(
query,
consistency_for_user(username),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{username},
true);
}).then_wrapped([=](future<::shared_ptr<cql3::untyped_result_set>> f) {
@@ -262,21 +263,46 @@ future<authenticated_user> password_authenticator::authenticate(
});
}
future<> password_authenticator::maybe_update_custom_options(std::string_view role_name, const authentication_options& options) const {
static const sstring query = format("UPDATE {} SET {} = ? WHERE {} = ?",
meta::roles_table::qualified_name,
OPTIONS,
meta::roles_table::role_col_name);
if (!options.options) {
return make_ready_future<>();
}
std::vector<std::pair<data_value, data_value>> entries;
for (const auto& entry : *options.options) {
entries.push_back({data_value(entry.first), data_value(entry.second)});
}
auto map_value = make_map_value(map_type_impl::get_instance(utf8_type, utf8_type, false), entries);
return _qp.execute_internal(
query,
consistency_for_user(role_name),
internal_distributed_query_state(),
{std::move(map_value), sstring(role_name)}).discard_result();
}
future<> password_authenticator::create(std::string_view role_name, const authentication_options& options) const {
if (!options.password) {
return make_ready_future<>();
return maybe_update_custom_options(role_name, options);
}
return _qp.execute_internal(
update_row_query(),
consistency_for_user(role_name),
internal_distributed_timeout_config(),
{passwords::hash(*options.password, rng_for_salt), sstring(role_name)}).discard_result();
internal_distributed_query_state(),
{passwords::hash(*options.password, rng_for_salt), sstring(role_name)}).discard_result().then([this, role_name, &options] {
return maybe_update_custom_options(role_name, options);
});
}
future<> password_authenticator::alter(std::string_view role_name, const authentication_options& options) const {
if (!options.password) {
return make_ready_future<>();
return maybe_update_custom_options(role_name, options);
}
static const sstring query = format("UPDATE {} SET {} = ? WHERE {} = ?",
@@ -287,8 +313,10 @@ future<> password_authenticator::alter(std::string_view role_name, const authent
return _qp.execute_internal(
query,
consistency_for_user(role_name),
internal_distributed_timeout_config(),
{passwords::hash(*options.password, rng_for_salt), sstring(role_name)}).discard_result();
internal_distributed_query_state(),
{passwords::hash(*options.password, rng_for_salt), sstring(role_name)}).discard_result().then([this, role_name, &options] {
return maybe_update_custom_options(role_name, options);
}).discard_result();
}
future<> password_authenticator::drop(std::string_view name) const {
@@ -299,12 +327,27 @@ future<> password_authenticator::drop(std::string_view name) const {
return _qp.execute_internal(
query, consistency_for_user(name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(name)}).discard_result();
}
future<custom_options> password_authenticator::query_custom_options(std::string_view role_name) const {
return make_ready_future<custom_options>();
static const sstring query = format("SELECT {} FROM {} WHERE {} = ?",
OPTIONS,
meta::roles_table::qualified_name,
meta::roles_table::role_col_name);
return _qp.execute_internal(
query, consistency_for_user(role_name),
internal_distributed_query_state(),
{sstring(role_name)}).then([](::shared_ptr<cql3::untyped_result_set> rs) {
custom_options opts;
const auto& row = rs->one();
if (row.has(OPTIONS)) {
row.get_map_data<sstring, sstring>(OPTIONS, std::inserter(opts, opts.end()), utf8_type, utf8_type);
}
return opts;
});
}
const resource_set& password_authenticator::protected_resources() const {

View File

@@ -94,6 +94,8 @@ public:
virtual ::shared_ptr<sasl_challenge> new_sasl_challenge() const override;
private:
future<> maybe_update_custom_options(std::string_view role_name, const authentication_options& options) const;
bool legacy_metadata_exists() const;
future<> migrate_legacy_metadata() const;

View File

@@ -43,7 +43,8 @@ std::string_view creation_query() {
" can_login boolean,"
" is_superuser boolean,"
" member_of set<text>,"
" salted_hash text"
" salted_hash text,"
" options frozen<map<text, text>>,"
")",
qualified_name,
role_col_name);
@@ -68,14 +69,13 @@ future<bool> default_role_row_satisfies(
return qp.execute_internal(
query,
db::consistency_level::ONE,
infinite_timeout_config,
{meta::DEFAULT_SUPERUSER_NAME},
true).then([&qp, &p](::shared_ptr<cql3::untyped_result_set> results) {
if (results->empty()) {
return qp.execute_internal(
query,
db::consistency_level::QUORUM,
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{meta::DEFAULT_SUPERUSER_NAME},
true).then([&p](::shared_ptr<cql3::untyped_result_set> results) {
if (results->empty()) {
@@ -100,7 +100,7 @@ future<bool> any_nondefault_role_row_satisfies(
return qp.execute_internal(
query,
db::consistency_level::QUORUM,
internal_distributed_timeout_config()).then([&p](::shared_ptr<cql3::untyped_result_set> results) {
internal_distributed_query_state()).then([&p](::shared_ptr<cql3::untyped_result_set> results) {
if (results->empty()) {
return false;
}

View File

@@ -154,7 +154,7 @@ future<> service::create_keyspace_if_missing(::service::migration_manager& mm) c
// We use min_timestamp so that default keyspace metadata will loose with any manual adjustments.
// See issue #2129.
return mm.announce_new_keyspace(ksm, api::min_timestamp);
return mm.announce_new_keyspace(ksm, api::min_timestamp, false);
}
return make_ready_future<>();
@@ -210,7 +210,6 @@ future<bool> service::has_existing_legacy_users() const {
return _qp.execute_internal(
default_user_query,
db::consistency_level::ONE,
infinite_timeout_config,
{meta::DEFAULT_SUPERUSER_NAME},
true).then([this](auto results) {
if (!results->empty()) {
@@ -220,7 +219,6 @@ future<bool> service::has_existing_legacy_users() const {
return _qp.execute_internal(
default_user_query,
db::consistency_level::QUORUM,
infinite_timeout_config,
{meta::DEFAULT_SUPERUSER_NAME},
true).then([this](auto results) {
if (!results->empty()) {
@@ -229,8 +227,7 @@ future<bool> service::has_existing_legacy_users() const {
return _qp.execute_internal(
all_users_query,
db::consistency_level::QUORUM,
infinite_timeout_config).then([](auto results) {
db::consistency_level::QUORUM).then([](auto results) {
return make_ready_future<bool>(!results->empty());
});
});

View File

@@ -86,7 +86,7 @@ static future<std::optional<record>> find_record(cql3::query_processor& qp, std:
return qp.execute_internal(
query,
consistency_for_role(role_name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(role_name)},
true).then([](::shared_ptr<cql3::untyped_result_set> results) {
if (results->empty()) {
@@ -165,7 +165,7 @@ future<> standard_role_manager::create_default_role_if_missing() const {
return _qp.execute_internal(
query,
db::consistency_level::QUORUM,
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{meta::DEFAULT_SUPERUSER_NAME}).then([](auto&&) {
log.info("Created default superuser role '{}'.", meta::DEFAULT_SUPERUSER_NAME);
return make_ready_future<>();
@@ -192,7 +192,7 @@ future<> standard_role_manager::migrate_legacy_metadata() const {
return _qp.execute_internal(
query,
db::consistency_level::QUORUM,
internal_distributed_timeout_config()).then([this](::shared_ptr<cql3::untyped_result_set> results) {
internal_distributed_query_state()).then([this](::shared_ptr<cql3::untyped_result_set> results) {
return do_for_each(*results, [this](const cql3::untyped_result_set_row& row) {
role_config config;
config.is_superuser = row.get_or<bool>("super", false);
@@ -253,7 +253,7 @@ future<> standard_role_manager::create_or_replace(std::string_view role_name, co
return _qp.execute_internal(
query,
consistency_for_role(role_name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(role_name), c.is_superuser, c.can_login},
true).discard_result();
}
@@ -296,7 +296,7 @@ standard_role_manager::alter(std::string_view role_name, const role_config_updat
build_column_assignments(u),
meta::roles_table::role_col_name),
consistency_for_role(role_name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(role_name)}).discard_result();
});
}
@@ -315,7 +315,7 @@ future<> standard_role_manager::drop(std::string_view role_name) const {
return _qp.execute_internal(
query,
consistency_for_role(role_name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(role_name)}).then([this, role_name](::shared_ptr<cql3::untyped_result_set> members) {
return parallel_for_each(
members->begin(),
@@ -354,7 +354,7 @@ future<> standard_role_manager::drop(std::string_view role_name) const {
return _qp.execute_internal(
query,
consistency_for_role(role_name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(role_name)}).discard_result();
};
@@ -381,7 +381,7 @@ standard_role_manager::modify_membership(
return _qp.execute_internal(
query,
consistency_for_role(grantee_name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{role_set{sstring(role_name)}, sstring(grantee_name)}).discard_result();
};
@@ -392,7 +392,7 @@ standard_role_manager::modify_membership(
format("INSERT INTO {} (role, member) VALUES (?, ?)",
meta::role_members_table::qualified_name),
consistency_for_role(role_name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(role_name), sstring(grantee_name)}).discard_result();
case membership_change::remove:
@@ -400,7 +400,7 @@ standard_role_manager::modify_membership(
format("DELETE FROM {} WHERE role = ? AND member = ?",
meta::role_members_table::qualified_name),
consistency_for_role(role_name),
internal_distributed_timeout_config(),
internal_distributed_query_state(),
{sstring(role_name), sstring(grantee_name)}).discard_result();
}
@@ -503,7 +503,7 @@ future<role_set> standard_role_manager::query_all() const {
return _qp.execute_internal(
query,
db::consistency_level::QUORUM,
internal_distributed_timeout_config()).then([](::shared_ptr<cql3::untyped_result_set> results) {
internal_distributed_query_state()).then([](::shared_ptr<cql3::untyped_result_set> results) {
role_set roles;
std::transform(

View File

@@ -28,7 +28,6 @@
#include <iosfwd>
#include <functional>
#include "utils/mutable_view.hh"
#include <xxhash.h>
using bytes = basic_sstring<int8_t, uint32_t, 31, false>;
using bytes_view = std::basic_string_view<int8_t>;
@@ -36,10 +35,6 @@ using bytes_mutable_view = basic_mutable_view<bytes_view::value_type>;
using bytes_opt = std::optional<bytes>;
using sstring_view = std::string_view;
inline bytes to_bytes(bytes&& b) {
return std::move(b);
}
inline sstring_view to_sstring_view(bytes_view view) {
return {reinterpret_cast<const char*>(view.data()), view.size()};
}
@@ -48,6 +43,17 @@ inline bytes_view to_bytes_view(sstring_view view) {
return {reinterpret_cast<const int8_t*>(view.data()), view.size()};
}
namespace std {
template <>
struct hash<bytes_view> {
size_t operator()(bytes_view v) const {
return hash<sstring_view>()({reinterpret_cast<const char*>(v.begin()), v.size()});
}
};
}
struct fmt_hex {
bytes_view& v;
fmt_hex(bytes_view& v) noexcept : v(v) {}
@@ -88,30 +94,6 @@ struct appending_hash<bytes_view> {
}
};
struct bytes_view_hasher : public hasher {
XXH64_state_t _state;
bytes_view_hasher(uint64_t seed = 0) noexcept {
XXH64_reset(&_state, seed);
}
void update(const char* ptr, size_t length) noexcept {
XXH64_update(&_state, ptr, length);
}
size_t finalize() {
return static_cast<size_t>(XXH64_digest(&_state));
}
};
namespace std {
template <>
struct hash<bytes_view> {
size_t operator()(bytes_view v) const {
bytes_view_hasher h;
appending_hash<bytes_view>{}(h, v);
return h.finalize();
}
};
} // namespace std
inline int32_t compare_unsigned(bytes_view v1, bytes_view v2) {
auto size = std::min(v1.size(), v2.size());
if (size) {

View File

@@ -461,7 +461,7 @@ void cache_flat_mutation_reader::maybe_add_to_cache(const clustering_row& cr) {
cr.cells().prepare_hash(*_schema, column_kind::regular_column);
}
auto new_entry = alloc_strategy_unique_ptr<rows_entry>(
current_allocator().construct<rows_entry>(*_schema, cr.key(), cr.as_deletable_row()));
current_allocator().construct<rows_entry>(*_schema, cr.key(), cr.tomb(), cr.marker(), cr.cells()));
new_entry->set_continuous(false);
auto it = _next_row.iterators_valid() ? _next_row.get_iterator_in_latest_version()
: mp.clustered_rows().lower_bound(cr.key(), less);

View File

@@ -220,7 +220,7 @@ public:
auto new_log_schema = create_log_schema(new_schema, log_schema ? std::make_optional(log_schema->id()) : std::nullopt);
auto log_mut = log_schema
? db::schema_tables::make_update_table_mutations(db, keyspace.metadata(), log_schema, new_log_schema, timestamp, false)
? db::schema_tables::make_update_table_mutations(keyspace.metadata(), log_schema, new_log_schema, timestamp, false)
: db::schema_tables::make_create_table_mutations(keyspace.metadata(), new_log_schema, timestamp)
;
@@ -579,6 +579,11 @@ db_context::builder& db_context::builder::with_migration_notifier(service::migra
return *this;
}
db_context::builder& db_context::builder::with_token_metadata(const locator::token_metadata& token_metadata) {
_token_metadata = token_metadata;
return *this;
}
db_context::builder& db_context::builder::with_cdc_metadata(cdc::metadata& cdc_metadata) {
_cdc_metadata = cdc_metadata;
return *this;
@@ -588,6 +593,7 @@ db_context db_context::builder::build() {
return db_context{
_proxy,
_migration_notifier ? _migration_notifier->get() : service::get_local_storage_service().get_migration_notifier(),
_token_metadata ? _token_metadata->get() : service::get_local_storage_service().get_token_metadata(),
_cdc_metadata ? _cdc_metadata->get() : service::get_local_storage_service().get_cdc_metadata(),
};
}
@@ -1291,13 +1297,6 @@ struct process_change_visitor {
_clustering_row_states, _generate_delta_values);
visit_row_cells(v);
if (_enable_updating_state) {
// #7716: if there are no regular columns, our visitor would not have visited any cells,
// hence it would not have created a row_state for this row. In effect, postimage wouldn't be produced.
// Ensure that the row state exists.
_clustering_row_states.try_emplace(ckey);
}
_builder.set_operation(log_ck, v._cdc_op);
_builder.set_ttl(log_ck, v._ttl_column);
}

View File

@@ -100,16 +100,19 @@ public:
struct db_context final {
service::storage_proxy& _proxy;
service::migration_notifier& _migration_notifier;
const locator::token_metadata& _token_metadata;
cdc::metadata& _cdc_metadata;
class builder final {
service::storage_proxy& _proxy;
std::optional<std::reference_wrapper<service::migration_notifier>> _migration_notifier;
std::optional<std::reference_wrapper<const locator::token_metadata>> _token_metadata;
std::optional<std::reference_wrapper<cdc::metadata>> _cdc_metadata;
public:
builder(service::storage_proxy& proxy);
builder& with_migration_notifier(service::migration_notifier& migration_notifier);
builder& with_token_metadata(const locator::token_metadata& token_metadata);
builder& with_cdc_metadata(cdc::metadata&);
db_context build();

View File

@@ -67,8 +67,8 @@ public:
int operator()(const clustering_key_prefix& p1, int32_t w1, const clustering_key_prefix& p2, int32_t w2) const {
auto type = _s.get().clustering_key_prefix_type();
auto res = prefix_equality_tri_compare(type->types().begin(),
type->begin(p1.representation()), type->end(p1.representation()),
type->begin(p2.representation()), type->end(p2.representation()),
type->begin(p1), type->end(p1),
type->begin(p2), type->end(p2),
::tri_compare);
if (res) {
return res;

View File

@@ -136,4 +136,4 @@ collection_mutation merge(const abstract_type&, collection_mutation_view, collec
collection_mutation difference(const abstract_type&, collection_mutation_view, collection_mutation_view);
// Serializes the given collection of cells to a sequence of bytes ready to be sent over the CQL protocol.
bytes_ostream serialize_for_cql(const abstract_type&, collection_mutation_view, cql_serialization_format);
bytes serialize_for_cql(const abstract_type&, collection_mutation_view, cql_serialization_format);

View File

@@ -73,19 +73,12 @@ private:
* <len(value1)><value1><len(value2)><value2>...<len(value_n)><value_n>
*
*/
template<typename RangeOfSerializedComponents, FragmentedMutableView Out>
static void serialize_value(RangeOfSerializedComponents&& values, Out out) {
template<typename RangeOfSerializedComponents, typename CharOutputIterator>
static void serialize_value(RangeOfSerializedComponents&& values, CharOutputIterator& out) {
for (auto&& val : values) {
assert(val.size() <= std::numeric_limits<size_type>::max());
write<size_type>(out, size_type(val.size()));
using val_type = std::remove_cvref_t<decltype(val)>;
if constexpr (FragmentedView<val_type>) {
write_fragmented(out, val);
} else if constexpr (std::same_as<val_type, managed_bytes>) {
write_fragmented(out, managed_bytes_view(val));
} else {
write_fragmented(out, single_fragmented_view(val));
}
out = std::copy(val.begin(), val.end(), out);
}
}
template <typename RangeOfSerializedComponents>
@@ -97,27 +90,25 @@ private:
return len;
}
public:
managed_bytes serialize_single(managed_bytes&& v) const {
return serialize_value({std::move(v)});
}
managed_bytes serialize_single(bytes&& v) const {
bytes serialize_single(bytes&& v) const {
return serialize_value({std::move(v)});
}
template<typename RangeOfSerializedComponents>
static managed_bytes serialize_value(RangeOfSerializedComponents&& values) {
static bytes serialize_value(RangeOfSerializedComponents&& values) {
auto size = serialized_size(values);
if (size > std::numeric_limits<size_type>::max()) {
throw std::runtime_error(format("Key size too large: {:d} > {:d}", size, std::numeric_limits<size_type>::max()));
}
managed_bytes b(managed_bytes::initialized_later(), size);
serialize_value(values, managed_bytes_mutable_view(b));
bytes b(bytes::initialized_later(), size);
auto i = b.begin();
serialize_value(values, i);
return b;
}
template<typename T>
static managed_bytes serialize_value(std::initializer_list<T> values) {
static bytes serialize_value(std::initializer_list<T> values) {
return serialize_value(boost::make_iterator_range(values.begin(), values.end()));
}
managed_bytes serialize_optionals(const std::vector<bytes_opt>& values) const {
bytes serialize_optionals(const std::vector<bytes_opt>& values) const {
return serialize_value(values | boost::adaptors::transformed([] (const bytes_opt& bo) -> bytes_view {
if (!bo) {
throw std::logic_error("attempted to create key component from empty optional");
@@ -125,7 +116,7 @@ public:
return *bo;
}));
}
managed_bytes serialize_value_deep(const std::vector<data_value>& values) const {
bytes serialize_value_deep(const std::vector<data_value>& values) const {
// TODO: Optimize
std::vector<bytes> partial;
partial.reserve(values.size());
@@ -136,26 +127,25 @@ public:
}
return serialize_value(partial);
}
managed_bytes decompose_value(const value_type& values) const {
bytes decompose_value(const value_type& values) const {
return serialize_value(values);
}
class iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = const managed_bytes_view;
using value_type = const bytes_view;
using difference_type = std::ptrdiff_t;
using pointer = const value_type*;
using reference = const value_type&;
using pointer = const bytes_view*;
using reference = const bytes_view&;
private:
managed_bytes_view _v;
managed_bytes_view _current;
size_t _remaining = 0;
bytes_view _v;
bytes_view _current;
private:
void read_current() {
_remaining = _v.size_bytes();
size_type len;
{
if (_v.empty()) {
_v = bytes_view(nullptr, 0);
return;
}
len = read_simple<size_type>(_v);
@@ -163,16 +153,15 @@ public:
throw_with_backtrace<marshal_exception>(format("compound_type iterator - not enough bytes, expected {:d}, got {:d}", len, _v.size()));
}
}
_current = _v.prefix(len);
_v.remove_prefix(_current.size_bytes());
_current = bytes_view(_v.begin(), len);
_v.remove_prefix(len);
}
public:
struct end_iterator_tag {};
iterator(const managed_bytes_view& v) : _v(v) {
iterator(const bytes_view& v) : _v(v) {
read_current();
}
iterator(end_iterator_tag, const managed_bytes_view& v) : _v() {}
iterator() {}
iterator(end_iterator_tag, const bytes_view& v) : _v(nullptr, 0) {}
iterator& operator++() {
read_current();
return *this;
@@ -184,40 +173,29 @@ public:
}
const value_type& operator*() const { return _current; }
const value_type* operator->() const { return &_current; }
bool operator==(const iterator& i) const { return _remaining == i._remaining; }
bool operator!=(const iterator& i) const { return _v.begin() != i._v.begin(); }
bool operator==(const iterator& i) const { return _v.begin() == i._v.begin(); }
};
static iterator begin(managed_bytes_view v) {
static iterator begin(const bytes_view& v) {
return iterator(v);
}
static iterator end(managed_bytes_view v) {
static iterator end(const bytes_view& v) {
return iterator(typename iterator::end_iterator_tag(), v);
}
static boost::iterator_range<iterator> components(managed_bytes_view v) {
static boost::iterator_range<iterator> components(const bytes_view& v) {
return { begin(v), end(v) };
}
value_type deserialize_value(managed_bytes_view v) const {
value_type deserialize_value(bytes_view v) const {
std::vector<bytes> result;
result.reserve(_types.size());
std::transform(begin(v), end(v), std::back_inserter(result), [] (auto&& v) {
return to_bytes(v);
return bytes(v.begin(), v.end());
});
return result;
}
bool less(managed_bytes_view b1, managed_bytes_view b2) const {
return with_linearized(b1, [&] (bytes_view bv1) {
return with_linearized(b2, [&] (bytes_view bv2) {
return less(bv1, bv2);
});
});
}
bool less(bytes_view b1, bytes_view b2) const {
return compare(b1, b2) < 0;
}
size_t hash(managed_bytes_view v) const{
return with_linearized(v, [&] (bytes_view v) {
return hash(v);
});
}
size_t hash(bytes_view v) const {
if (_byte_order_equal) {
return std::hash<bytes_view>()(v);
@@ -230,13 +208,6 @@ public:
}
return h;
}
int compare(managed_bytes_view b1, managed_bytes_view b2) const {
return with_linearized(b1, [&] (bytes_view bv1) {
return with_linearized(b2, [&] (bytes_view bv2) {
return compare(bv1, bv2);
});
});
}
int compare(bytes_view b1, bytes_view b2) const {
if (_byte_order_comparable) {
if (_is_reversed) {
@@ -251,21 +222,15 @@ public:
});
}
// Retruns true iff given prefix has no missing components
bool is_full(managed_bytes_view v) const {
bool is_full(bytes_view v) const {
assert(AllowPrefixes == allow_prefixes::yes);
return std::distance(begin(v), end(v)) == (ssize_t)_types.size();
}
bool is_empty(managed_bytes_view v) const {
return v.empty();
}
bool is_empty(const managed_bytes& v) const {
return v.empty();
}
bool is_empty(bytes_view v) const {
return begin(v) == end(v);
}
void validate(managed_bytes_view v) const {
std::vector<managed_bytes_view> values(begin(v), end(v));
void validate(bytes_view v) const {
std::vector<bytes_view> values(begin(v), end(v));
if (AllowPrefixes == allow_prefixes::no && values.size() < _types.size()) {
throw marshal_exception(fmt::format("compound::validate(): non-prefixable compound cannot be a prefix"));
}
@@ -278,13 +243,6 @@ public:
_types[i]->validate(values[i], cql_serialization_format::internal());
}
}
bool equal(managed_bytes_view v1, managed_bytes_view v2) const {
return with_linearized(v1, [&] (bytes_view bv1) {
return with_linearized(v2, [&] (bytes_view bv2) {
return equal(bv1, bv2);
});
});
}
bool equal(bytes_view v1, bytes_view v2) const {
if (_byte_order_equal) {
return compare_unsigned(v1, v2) == 0;

View File

@@ -54,9 +54,9 @@ template <typename CompoundType>
class legacy_compound_view {
static_assert(!CompoundType::is_prefixable, "Legacy view not defined for prefixes");
CompoundType& _type;
managed_bytes_view _packed;
bytes_view _packed;
public:
legacy_compound_view(CompoundType& c, managed_bytes_view packed)
legacy_compound_view(CompoundType& c, bytes_view packed)
: _type(c)
, _packed(packed)
{ }
@@ -147,18 +147,18 @@ public:
{ }
// @k1 and @k2 must be serialized using @type, which was passed to the constructor.
int operator()(managed_bytes_view k1, managed_bytes_view k2) const {
int operator()(bytes_view k1, bytes_view k2) const {
if (_type.is_singular()) {
return compare_unsigned(*_type.begin(k1), *_type.begin(k2));
}
return lexicographical_tri_compare(
_type.begin(k1), _type.end(k1),
_type.begin(k2), _type.end(k2),
[] (const managed_bytes_view& c1, const managed_bytes_view& c2) -> int {
[] (const bytes_view& c1, const bytes_view& c2) -> int {
if (c1.size() != c2.size() || !c1.size()) {
return c1.size() < c2.size() ? -1 : c1.size() ? 1 : 0;
}
return compare_unsigned(c1, c2);
return memcmp(c1.begin(), c2.begin(), c1.size());
});
}
};
@@ -188,7 +188,7 @@ public:
// @packed is assumed to be serialized using supplied @type.
template <typename CompoundType>
static inline
bytes to_legacy(CompoundType& type, managed_bytes_view packed) {
bytes to_legacy(CompoundType& type, bytes_view packed) {
legacy_compound_view<CompoundType> lv(type, packed);
bytes legacy_form(bytes::initialized_later(), lv.size());
std::copy(lv.begin(), lv.end(), legacy_form.begin());
@@ -264,12 +264,6 @@ private:
static void write_value(Value&& val, CharOutputIterator& out) {
out = std::copy(val.begin(), val.end(), out);
}
template<typename CharOutputIterator>
static void write_value(managed_bytes_view val, CharOutputIterator& out) {
for (bytes_view frag : fragment_range(val)) {
out = std::copy(frag.begin(), frag.end(), out);
}
}
template <typename CharOutputIterator>
static void write_value(const data_value& val, CharOutputIterator& out) {
val.serialize(out);
@@ -411,7 +405,6 @@ public:
iterator(end_iterator_tag) : _v(nullptr, 0) {}
public:
iterator() : iterator(end_iterator_tag()) {}
iterator& operator++() {
read_current();
return *this;

View File

@@ -59,9 +59,6 @@ i18n_xlat = {
}
python3_dependencies = subprocess.run('./install-dependencies.sh --print-python3-runtime-packages', shell=True, capture_output=True, encoding='utf-8').stdout.strip()
node_exporter_filename = subprocess.run('./install-dependencies.sh --print-node-exporter-filename', shell=True, capture_output=True, encoding='utf-8').stdout.strip()
node_exporter_dirname = os.path.basename(node_exporter_filename).rstrip('.tar.gz')
def pkgname(name):
if name in i18n_xlat:
@@ -265,7 +262,7 @@ modes = {
'stack-usage-threshold': 1024*13,
},
'dev': {
'cxxflags': '-O1 -DDEVEL -DSEASTAR_ENABLE_ALLOC_FAILURE_INJECTION -DSCYLLA_ENABLE_ERROR_INJECTION',
'cxxflags': '-O1 -DSEASTAR_ENABLE_ALLOC_FAILURE_INJECTION -DSCYLLA_ENABLE_ERROR_INJECTION',
'cxx_ld_flags': '',
'stack-usage-threshold': 1024*21,
},
@@ -332,7 +329,6 @@ scylla_tests = set([
'test/boost/gossip_test',
'test/boost/gossiping_property_file_snitch_test',
'test/boost/hash_test',
'test/boost/hashers_test',
'test/boost/idl_test',
'test/boost/imr_test',
'test/boost/input_stream_test',
@@ -348,7 +344,6 @@ scylla_tests = set([
'test/boost/estimated_histogram_test',
'test/boost/logalloc_test',
'test/boost/managed_vector_test',
'test/boost/managed_bytes_test',
'test/boost/intrusive_array_test',
'test/boost/map_difference_test',
'test/boost/memtable_test',
@@ -455,7 +450,6 @@ apps = set([
'scylla',
'test/tools/cql_repl',
'tools/scylla-types',
'tools/scylla-sstable-index',
])
tests = scylla_tests | perf_tests | raft_tests
@@ -581,7 +575,6 @@ scylla_core = (['database.cc',
'sstables/mp_row_consumer.cc',
'sstables/sstables.cc',
'sstables/sstables_manager.cc',
'sstables/sstable_set.cc',
'sstables/mx/writer.cc',
'sstables/kl/writer.cc',
'sstables/sstable_version.cc',
@@ -989,7 +982,6 @@ deps = {
'test/tools/cql_repl': idls + ['test/tools/cql_repl.cc'] + scylla_core + scylla_tests_generic_dependencies,
#FIXME: we don't need all of scylla_core here, only the types module, need to modularize scylla_core.
'tools/scylla-types': idls + ['tools/scylla-types.cc'] + scylla_core,
'tools/scylla-sstable-index': idls + ['tools/scylla-sstable-index.cc'] + scylla_core,
}
pure_boost_tests = set([
@@ -1009,7 +1001,6 @@ pure_boost_tests = set([
'test/boost/dynamic_bitset_test',
'test/boost/enum_option_test',
'test/boost/enum_set_test',
'test/boost/hashers_test',
'test/boost/idl_test',
'test/boost/json_test',
'test/boost/keys_test',
@@ -1026,7 +1017,6 @@ pure_boost_tests = set([
'test/boost/top_k_test',
'test/boost/vint_serialization_test',
'test/boost/bptree_test',
'test/boost/utf8_test',
'test/manual/streaming_histogram_test',
])
@@ -1149,6 +1139,7 @@ warnings = [
'-Wno-delete-non-abstract-non-virtual-dtor',
'-Wno-unknown-attributes',
'-Wno-braced-scalar-init',
'-Wno-unused-value',
'-Wno-range-loop-construct',
'-Wno-unused-function',
'-Wno-implicit-int-float-conversion',
@@ -1807,7 +1798,7 @@ with open(buildfile_tmp, 'w') as f:
f.write(textwrap.dedent('''\
build $builddir/{mode}/iotune: copy $builddir/{mode}/seastar/apps/iotune/iotune
''').format(**locals()))
f.write('build $builddir/{mode}/dist/tar/{scylla_product}-package.tar.gz: package $builddir/{mode}/scylla $builddir/{mode}/iotune $builddir/SCYLLA-RELEASE-FILE $builddir/SCYLLA-VERSION-FILE $builddir/debian/debian $builddir/node_exporter | always\n'.format(**locals()))
f.write('build $builddir/{mode}/dist/tar/{scylla_product}-package.tar.gz: package $builddir/{mode}/scylla $builddir/{mode}/iotune $builddir/SCYLLA-RELEASE-FILE $builddir/SCYLLA-VERSION-FILE $builddir/debian/debian | always\n'.format(**locals()))
f.write(' mode = {mode}\n'.format(**locals()))
f.write(f'build $builddir/dist/{mode}/redhat: rpmbuild $builddir/{mode}/dist/tar/{scylla_product}-package.tar.gz\n')
f.write(f' mode = {mode}\n')
@@ -1966,9 +1957,6 @@ with open(buildfile_tmp, 'w') as f:
rule debian_files_gen
command = ./dist/debian/debian_files_gen.py
build $builddir/debian/debian: debian_files_gen | always
rule extract_node_exporter
command = tar -C build -xvpf {node_exporter_filename} && rm -rfv build/node_exporter && mv -v build/{node_exporter_dirname} build/node_exporter
build $builddir/node_exporter: extract_node_exporter | always
''').format(**globals()))
os.rename(buildfile_tmp, buildfile)

View File

@@ -19,10 +19,16 @@
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#include "service/storage_service.hh"
#include "counters.hh"
#include "mutation.hh"
#include "combine.hh"
counter_id counter_id::local()
{
return counter_id(service::get_local_storage_service().get_local_id());
}
std::ostream& operator<<(std::ostream& os, const counter_id& id) {
return os << id.to_uuid();
}
@@ -191,10 +197,10 @@ std::optional<atomic_cell> counter_cell_view::difference(atomic_cell_view a, ato
}
void transform_counter_updates_to_shards(mutation& m, const mutation* current_state, uint64_t clock_offset, utils::UUID local_id) {
void transform_counter_updates_to_shards(mutation& m, const mutation* current_state, uint64_t clock_offset) {
// FIXME: allow current_state to be frozen_mutation
auto transform_new_row_to_shards = [&s = *m.schema(), clock_offset, local_id] (column_kind kind, auto& cells) {
auto transform_new_row_to_shards = [&s = *m.schema(), clock_offset] (column_kind kind, auto& cells) {
cells.for_each_cell([&] (column_id id, atomic_cell_or_collection& ac_o_c) {
auto& cdef = s.column_at(kind, id);
auto acv = ac_o_c.as_atomic_cell(cdef);
@@ -202,7 +208,7 @@ void transform_counter_updates_to_shards(mutation& m, const mutation* current_st
return; // continue -- we are in lambda
}
auto delta = acv.counter_update_value();
auto cs = counter_shard(counter_id(local_id), delta, clock_offset + 1);
auto cs = counter_shard(counter_id::local(), delta, clock_offset + 1);
ac_o_c = counter_cell_builder::from_single_shard(acv.timestamp(), cs);
});
};
@@ -217,7 +223,7 @@ void transform_counter_updates_to_shards(mutation& m, const mutation* current_st
clustering_key::less_compare cmp(*m.schema());
auto transform_row_to_shards = [&s = *m.schema(), clock_offset, local_id] (column_kind kind, auto& transformee, auto& state) {
auto transform_row_to_shards = [&s = *m.schema(), clock_offset] (column_kind kind, auto& transformee, auto& state) {
std::deque<std::pair<column_id, counter_shard>> shards;
state.for_each_cell([&] (column_id id, const atomic_cell_or_collection& ac_o_c) {
auto& cdef = s.column_at(kind, id);
@@ -226,7 +232,7 @@ void transform_counter_updates_to_shards(mutation& m, const mutation* current_st
return; // continue -- we are in lambda
}
counter_cell_view::with_linearized(acv, [&] (counter_cell_view ccv) {
auto cs = ccv.get_shard(counter_id(local_id));
auto cs = ccv.local_shard();
if (!cs) {
return; // continue
}
@@ -247,7 +253,7 @@ void transform_counter_updates_to_shards(mutation& m, const mutation* current_st
auto delta = acv.counter_update_value();
if (shards.empty() || shards.front().first > id) {
auto cs = counter_shard(counter_id(local_id), delta, clock_offset + 1);
auto cs = counter_shard(counter_id::local(), delta, clock_offset + 1);
ac_o_c = counter_cell_builder::from_single_shard(acv.timestamp(), cs);
} else {
auto& cs = shards.front().second;

View File

@@ -61,6 +61,8 @@ public:
return !(*this == other);
}
public:
static counter_id local();
// For tests.
static counter_id generate_random() {
return counter_id(utils::make_random_uuid());
@@ -403,6 +405,11 @@ public:
return *it;
}
std::optional<counter_shard_view> local_shard() const {
// TODO: consider caching local shard position
return get_shard(counter_id::local());
}
bool operator==(const basic_counter_cell_view& other) const {
return timestamp() == other.timestamp() && boost::equal(shards(), other.shards());
}
@@ -444,7 +451,7 @@ struct counter_cell_mutable_view : basic_counter_cell_view<mutable_view::yes> {
// Transforms mutation dst from counter updates to counter shards using state
// stored in current_state.
// If current_state is present it has to be in the same schema as dst.
void transform_counter_updates_to_shards(mutation& dst, const mutation* current_state, uint64_t clock_offset, utils::UUID local_id);
void transform_counter_updates_to_shards(mutation& dst, const mutation* current_state, uint64_t clock_offset);
template<>
struct appending_hash<counter_shard_view> {

View File

@@ -394,7 +394,6 @@ selectStatement returns [std::unique_ptr<raw::select_statement> expr]
bool allow_filtering = false;
bool is_json = false;
bool bypass_cache = false;
auto attrs = std::make_unique<cql3::attributes::raw>();
}
: K_SELECT (
( K_JSON { is_json = true; } )?
@@ -409,12 +408,11 @@ selectStatement returns [std::unique_ptr<raw::select_statement> expr]
( K_LIMIT rows=intValue { limit = rows; } )?
( K_ALLOW K_FILTERING { allow_filtering = true; } )?
( K_BYPASS K_CACHE { bypass_cache = true; })?
( usingClause[attrs] )?
{
auto params = make_lw_shared<raw::select_statement::parameters>(std::move(orderings), is_distinct, allow_filtering, is_json, bypass_cache);
$expr = std::make_unique<raw::select_statement>(std::move(cf), std::move(params),
std::move(sclause), std::move(wclause), std::move(limit), std::move(per_partition_limit),
std::move(gbcolumns), std::move(attrs));
std::move(gbcolumns));
}
;
@@ -523,7 +521,6 @@ usingClause[std::unique_ptr<cql3::attributes::raw>& attrs]
usingClauseObjective[std::unique_ptr<cql3::attributes::raw>& attrs]
: K_TIMESTAMP ts=intValue { attrs->timestamp = ts; }
| K_TTL t=intValue { attrs->time_to_live = t; }
| K_TIMEOUT to=term { attrs->timeout = to; }
;
/**
@@ -1764,7 +1761,6 @@ basic_unreserved_keyword returns [sstring str]
| K_PER
| K_PARTITION
| K_GROUP
| K_TIMEOUT
) { $str = $k.text; }
;
@@ -1920,8 +1916,6 @@ K_GROUP: G R O U P;
K_LIKE: L I K E;
K_TIMEOUT: T I M E O U T;
// Case-insensitive alpha characters
fragment A: ('a'|'A');
fragment B: ('b'|'B');

View File

@@ -70,11 +70,11 @@ abstract_marker::raw::raw(int32_t bind_index)
::shared_ptr<term> abstract_marker::raw::prepare(database& db, const sstring& keyspace, lw_shared_ptr<column_specification> receiver) const
{
if (receiver->type->is_collection()) {
if (receiver->type->without_reversed().is_list()) {
if (receiver->type->get_kind() == abstract_type::kind::list) {
return ::make_shared<lists::marker>(_bind_index, receiver);
} else if (receiver->type->without_reversed().is_set()) {
} else if (receiver->type->get_kind() == abstract_type::kind::set) {
return ::make_shared<sets::marker>(_bind_index, receiver);
} else if (receiver->type->without_reversed().is_map()) {
} else if (receiver->type->get_kind() == abstract_type::kind::map) {
return ::make_shared<maps::marker>(_bind_index, receiver);
}
assert(0);

View File

@@ -44,13 +44,12 @@
namespace cql3 {
std::unique_ptr<attributes> attributes::none() {
return std::unique_ptr<attributes>{new attributes{{}, {}, {}}};
return std::unique_ptr<attributes>{new attributes{{}, {}}};
}
attributes::attributes(::shared_ptr<term>&& timestamp, ::shared_ptr<term>&& time_to_live, ::shared_ptr<term>&& timeout)
attributes::attributes(::shared_ptr<term>&& timestamp, ::shared_ptr<term>&& time_to_live)
: _timestamp{std::move(timestamp)}
, _time_to_live{std::move(time_to_live)}
, _timeout{std::move(timeout)}
{ }
bool attributes::is_timestamp_set() const {
@@ -61,10 +60,6 @@ bool attributes::is_time_to_live_set() const {
return bool(_time_to_live);
}
bool attributes::is_timeout_set() const {
return bool(_timeout);
}
int64_t attributes::get_timestamp(int64_t now, const query_options& options) {
if (!_timestamp) {
return now;
@@ -77,12 +72,14 @@ int64_t attributes::get_timestamp(int64_t now, const query_options& options) {
if (tval.is_unset_value()) {
return now;
}
return with_linearized(*tval, [&] (bytes_view val) {
try {
data_type_for<int64_t>()->validate(*tval, options.get_cql_serialization_format());
data_type_for<int64_t>()->validate(val, options.get_cql_serialization_format());
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception("Invalid timestamp value");
}
return value_cast<int64_t>(data_type_for<int64_t>()->deserialize(*tval));
return value_cast<int64_t>(data_type_for<int64_t>()->deserialize(val));
});
}
int32_t attributes::get_time_to_live(const query_options& options) {
@@ -96,15 +93,16 @@ int32_t attributes::get_time_to_live(const query_options& options) {
if (tval.is_unset_value()) {
return 0;
}
auto ttl = with_linearized(*tval, [&] (bytes_view val) {
try {
data_type_for<int32_t>()->validate(*tval, options.get_cql_serialization_format());
data_type_for<int32_t>()->validate(val, options.get_cql_serialization_format());
}
catch (marshal_exception& e) {
throw exceptions::invalid_request_exception("Invalid TTL value");
}
auto ttl = value_cast<int32_t>(data_type_for<int32_t>()->deserialize(*tval));
return value_cast<int32_t>(data_type_for<int32_t>()->deserialize(val));
});
if (ttl < 0) {
throw exceptions::invalid_request_exception("A TTL must be greater or equal to 0");
}
@@ -117,25 +115,6 @@ int32_t attributes::get_time_to_live(const query_options& options) {
return ttl;
}
db::timeout_clock::duration attributes::get_timeout(const query_options& options) const {
auto timeout = _timeout->bind_and_get(options);
if (timeout.is_null() || timeout.is_unset_value()) {
throw exceptions::invalid_request_exception("Timeout value cannot be unset/null");
}
cql_duration duration = value_cast<cql_duration>(duration_type->deserialize(*timeout));
if (duration.months || duration.days) {
throw exceptions::invalid_request_exception("Timeout values cannot be expressed in days/months");
}
if (duration.nanoseconds % 1'000'000 != 0) {
throw exceptions::invalid_request_exception("Timeout values cannot have granularity finer than milliseconds");
}
if (duration.nanoseconds < 0) {
throw exceptions::invalid_request_exception("Timeout values must be non-negative");
}
return std::chrono::duration_cast<db::timeout_clock::duration>(std::chrono::nanoseconds(duration.nanoseconds));
}
void attributes::collect_marker_specification(variable_specifications& bound_names) const {
if (_timestamp) {
_timestamp->collect_marker_specification(bound_names);
@@ -143,16 +122,12 @@ void attributes::collect_marker_specification(variable_specifications& bound_nam
if (_time_to_live) {
_time_to_live->collect_marker_specification(bound_names);
}
if (_timeout) {
_timeout->collect_marker_specification(bound_names);
}
}
std::unique_ptr<attributes> attributes::raw::prepare(database& db, const sstring& ks_name, const sstring& cf_name) const {
auto ts = !timestamp ? ::shared_ptr<term>{} : timestamp->prepare(db, ks_name, timestamp_receiver(ks_name, cf_name));
auto ttl = !time_to_live ? ::shared_ptr<term>{} : time_to_live->prepare(db, ks_name, time_to_live_receiver(ks_name, cf_name));
auto to = !timeout ? ::shared_ptr<term>{} : timeout->prepare(db, ks_name, timeout_receiver(ks_name, cf_name));
return std::unique_ptr<attributes>{new attributes{std::move(ts), std::move(ttl), std::move(to)}};
return std::unique_ptr<attributes>{new attributes{std::move(ts), std::move(ttl)}};
}
lw_shared_ptr<column_specification> attributes::raw::timestamp_receiver(const sstring& ks_name, const sstring& cf_name) const {
@@ -163,8 +138,4 @@ lw_shared_ptr<column_specification> attributes::raw::time_to_live_receiver(const
return make_lw_shared<column_specification>(ks_name, cf_name, ::make_shared<column_identifier>("[ttl]", true), data_type_for<int32_t>());
}
lw_shared_ptr<column_specification> attributes::raw::timeout_receiver(const sstring& ks_name, const sstring& cf_name) const {
return make_lw_shared<column_specification>(ks_name, cf_name, ::make_shared<column_identifier>("[timeout]", true), duration_type);
}
}

View File

@@ -54,39 +54,31 @@ class attributes final {
private:
const ::shared_ptr<term> _timestamp;
const ::shared_ptr<term> _time_to_live;
const ::shared_ptr<term> _timeout;
public:
static std::unique_ptr<attributes> none();
private:
attributes(::shared_ptr<term>&& timestamp, ::shared_ptr<term>&& time_to_live, ::shared_ptr<term>&& timeout);
attributes(::shared_ptr<term>&& timestamp, ::shared_ptr<term>&& time_to_live);
public:
bool is_timestamp_set() const;
bool is_time_to_live_set() const;
bool is_timeout_set() const;
int64_t get_timestamp(int64_t now, const query_options& options);
int32_t get_time_to_live(const query_options& options);
db::timeout_clock::duration get_timeout(const query_options& options) const;
void collect_marker_specification(variable_specifications& bound_names) const;
class raw final {
public:
::shared_ptr<term::raw> timestamp;
::shared_ptr<term::raw> time_to_live;
::shared_ptr<term::raw> timeout;
std::unique_ptr<attributes> prepare(database& db, const sstring& ks_name, const sstring& cf_name) const;
private:
lw_shared_ptr<column_specification> timestamp_receiver(const sstring& ks_name, const sstring& cf_name) const;
lw_shared_ptr<column_specification> time_to_live_receiver(const sstring& ks_name, const sstring& cf_name) const;
lw_shared_ptr<column_specification> timeout_receiver(const sstring& ks_name, const sstring& cf_name) const;
};
};

View File

@@ -192,12 +192,9 @@ public:
virtual ::shared_ptr<terminal> bind(const query_options& options) override {
auto bytes = bind_and_get(options);
if (bytes.is_null()) {
if (!bytes) {
return ::shared_ptr<terminal>{};
}
if (bytes.is_unset_value()) {
return UNSET_VALUE;
}
return ::make_shared<constants::value>(std::move(cql3::raw_value::make_value(to_bytes(*bytes))));
}
};
@@ -230,7 +227,9 @@ public:
} else if (value.is_unset_value()) {
return;
}
auto increment = value_cast<int64_t>(long_type->deserialize_value(*value));
auto increment = with_linearized(*value, [] (bytes_view value_view) {
return value_cast<int64_t>(long_type->deserialize_value(value_view));
});
m.set_cell(prefix, column, make_counter_update_cell(increment, params));
}
};
@@ -245,7 +244,9 @@ public:
} else if (value.is_unset_value()) {
return;
}
auto increment = value_cast<int64_t>(long_type->deserialize_value(*value));
auto increment = with_linearized(*value, [] (bytes_view value_view) {
return value_cast<int64_t>(long_type->deserialize_value(value_view));
});
if (increment == std::numeric_limits<int64_t>::min()) {
throw exceptions::invalid_request_exception(format("The negation of {:d} overflows supported counter precision (signed 8 bytes integer)", increment));
}

View File

@@ -27,7 +27,6 @@
#include <fmt/ostream.h>
#include <unordered_map>
#include "cql3/constants.hh"
#include "cql3/lists.hh"
#include "cql3/tuples.hh"
#include "index/secondary_index_manager.hh"
@@ -44,8 +43,7 @@ using boost::adaptors::transformed;
namespace {
static
bytes_opt do_get_value(const schema& schema,
std::optional<atomic_cell_value_view> do_get_value(const schema& schema,
const column_definition& cdef,
const partition_key& key,
const clustering_key_prefix& ckey,
@@ -53,9 +51,9 @@ bytes_opt do_get_value(const schema& schema,
gc_clock::time_point now) {
switch (cdef.kind) {
case column_kind::partition_key:
return to_bytes(key.get_component(schema, cdef.component_index()));
return atomic_cell_value_view(key.get_component(schema, cdef.component_index()));
case column_kind::clustering_key:
return to_bytes(ckey.get_component(schema, cdef.component_index()));
return atomic_cell_value_view(ckey.get_component(schema, cdef.component_index()));
default:
auto cell = cells.find_cell(cdef.id);
if (!cell) {
@@ -63,7 +61,7 @@ bytes_opt do_get_value(const schema& schema,
}
assert(cdef.is_atomic());
auto c = cell->as_atomic_cell(cdef);
return c.is_dead(now) ? std::nullopt : bytes_opt(c.value().linearize());
return c.is_dead(now) ? std::nullopt : std::optional<atomic_cell_value_view>(c.value());
}
}
@@ -140,8 +138,9 @@ bytes_opt get_value_from_partition_slice(
/// Returns col's value from a mutation.
bytes_opt get_value_from_mutation(const column_value& col, row_data_from_mutation data) {
return do_get_value(
const auto v = do_get_value(
data.schema_, *col.col, data.partition_key_, data.clustering_key_, data.other_columns, data.now);
return v ? v->linearize() : bytes_opt();
}
/// Returns col's value from the fetched data.
@@ -155,7 +154,7 @@ bytes_opt get_value(const column_value& col, const column_value_eval_bag& bag) {
/// Type for comparing results of get_value().
const abstract_type* get_value_comparator(const column_definition* cdef) {
return &cdef->type->without_reversed();
return cdef->type->is_reversed() ? cdef->type->underlying_type().get() : cdef->type.get();
}
/// Type for comparing results of get_value().
@@ -356,12 +355,16 @@ bytes_opt next_value(query::result_row_view::iterator_type& iter, const column_d
if (cdef->type->is_multi_cell()) {
auto cell = iter.next_collection_cell();
if (cell) {
return linearized(*cell);
return cell->with_linearized([] (bytes_view data) {
return bytes(data.cbegin(), data.cend());
});
}
} else {
auto cell = iter.next_atomic_cell();
if (cell) {
return linearized(cell->value());
return cell->value().with_linearized([] (bytes_view data) {
return bytes(data.cbegin(), data.cend());
});
}
}
return std::nullopt;
@@ -565,8 +568,7 @@ const auto deref = boost::adaptors::transformed([] (const bytes_opt& b) { return
/// Returns possible values from t, which must be RHS of IN.
value_list get_IN_values(
const ::shared_ptr<term>& t, const query_options& options, const serialized_compare& comparator,
sstring_view column_name) {
const ::shared_ptr<term>& t, const query_options& options, const serialized_compare& comparator) {
// RHS is prepared differently for different CQL cases. Cast it dynamically to discern which case this is.
if (auto dv = dynamic_pointer_cast<lists::delayed_value>(t)) {
// Case `a IN (1,2,3)`.
@@ -576,11 +578,8 @@ value_list get_IN_values(
return to_sorted_vector(std::move(result_range), comparator);
} else if (auto mkr = dynamic_pointer_cast<lists::marker>(t)) {
// Case `a IN ?`. Collect all list-element values.
const auto val = mkr->bind(options);
if (val == constants::UNSET_VALUE) {
throw exceptions::invalid_request_exception(format("Invalid unset value for column {}", column_name));
}
return to_sorted_vector(static_pointer_cast<lists::value>(val)->get_elements() | non_null | deref, comparator);
const auto val = static_pointer_cast<lists::value>(mkr->bind(options));
return to_sorted_vector(val->get_elements() | non_null | deref, comparator);
}
throw std::logic_error(format("get_IN_values(single column) on invalid term {}", *t));
}
@@ -687,7 +686,7 @@ value_set possible_lhs_values(const column_definition* cdef, const expression& e
return oper.op == oper_t::EQ ? value_set(value_list{*val})
: to_range(oper.op, *val);
} else if (oper.op == oper_t::IN) {
return get_IN_values(oper.rhs, options, type->as_less_comparator(), cdef->name_as_text());
return get_IN_values(oper.rhs, options, type->as_less_comparator());
}
throw std::logic_error(format("possible_lhs_values: unhandled operator {}", oper));
},
@@ -777,11 +776,9 @@ bool is_supported_by(const expression& expr, const secondary_index::index& idx)
return idx.supports_expression(*col.col, oper.op);
},
[&] (const std::vector<column_value>& cvs) {
if (cvs.size() == 1) {
return idx.supports_expression(*cvs[0].col, oper.op);
}
// We don't use index table for multi-column restrictions, as it cannot avoid filtering.
return false;
return boost::algorithm::any_of(cvs, [&] (const column_value& c) {
return idx.supports_expression(*c.col, oper.op);
});
},
[&] (const token&) { return false; },
}, oper.lhs);

View File

@@ -219,7 +219,7 @@ struct aggregate_type_for<simple_date_native_type> {
template<>
struct aggregate_type_for<timeuuid_native_type> {
using type = timeuuid_native_type;
using type = timeuuid_native_type::primary_type;
};
template<>
@@ -227,7 +227,6 @@ struct aggregate_type_for<time_native_type> {
using type = time_native_type::primary_type;
};
// WARNING: never invoke this on temporary values; it will return a dangling reference.
template <typename Type>
const Type& max_wrapper(const Type& t1, const Type& t2) {
using std::max;
@@ -242,10 +241,6 @@ inline const net::inet_address& max_wrapper(const net::inet_address& t1, const n
return std::memcmp(t1.data(), t2.data(), len) >= 0 ? t1 : t2;
}
inline const timeuuid_native_type& max_wrapper(const timeuuid_native_type& t1, const timeuuid_native_type& t2) {
return t1.uuid.timestamp() > t2.uuid.timestamp() ? t1 : t2;
}
template <typename Type>
class impl_max_function_for final : public aggregate_function::aggregate {
std::optional<typename aggregate_type_for<Type>::type> _max{};
@@ -328,7 +323,6 @@ make_max_function() {
return make_shared<max_function_for<Type>>();
}
// WARNING: never invoke this on temporary values; it will return a dangling reference.
template <typename Type>
const Type& min_wrapper(const Type& t1, const Type& t2) {
using std::min;
@@ -343,10 +337,6 @@ inline const net::inet_address& min_wrapper(const net::inet_address& t1, const n
return std::memcmp(t1.data(), t2.data(), len) <= 0 ? t1 : t2;
}
inline timeuuid_native_type min_wrapper(timeuuid_native_type t1, timeuuid_native_type t2) {
return t1.uuid.timestamp() < t2.uuid.timestamp() ? t1 : t2;
}
template <typename Type>
class impl_min_function_for final : public aggregate_function::aggregate {
std::optional<typename aggregate_type_for<Type>::type> _min{};

View File

@@ -40,7 +40,7 @@ lw_shared_ptr<column_specification>
lists::value_spec_of(const column_specification& column) {
return make_lw_shared<column_specification>(column.ks_name, column.cf_name,
::make_shared<column_identifier>(format("value({})", *column.name), true),
dynamic_cast<const list_type_impl&>(column.type->without_reversed()).get_elements_type());
dynamic_pointer_cast<const list_type_impl>(column.type)->get_elements_type());
}
lw_shared_ptr<column_specification>
@@ -87,7 +87,7 @@ lists::literal::prepare(database& db, const sstring& keyspace, lw_shared_ptr<col
void
lists::literal::validate_assignable_to(database& db, const sstring keyspace, const column_specification& receiver) const {
if (!receiver.type->without_reversed().is_list()) {
if (!dynamic_pointer_cast<const list_type_impl>(receiver.type)) {
throw exceptions::invalid_request_exception(format("Invalid list literal for {} of type {}",
*receiver.name, receiver.type->as_cql3_type()));
}
@@ -125,11 +125,18 @@ lists::literal::to_string() const {
lists::value
lists::value::from_serialized(const fragmented_temporary_buffer::view& val, const list_type_impl& type, cql_serialization_format sf) {
return with_linearized(val, [&] (bytes_view v) {
return from_serialized(v, type, sf);
});
}
lists::value
lists::value::from_serialized(bytes_view v, const list_type_impl& type, cql_serialization_format sf) {
try {
// Collections have this small hack that validate cannot be called on a serialized object,
// but compose does the validation (so we're fine).
// FIXME: deserializeForNativeProtocol()?!
auto l = value_cast<list_type_impl::native_type>(type.deserialize(val, sf));
auto l = value_cast<list_type_impl::native_type>(type.deserialize(v, sf));
std::vector<bytes_opt> elements;
elements.reserve(l.size());
for (auto&& element : l) {
@@ -220,15 +227,17 @@ lists::delayed_value::bind(const query_options& options) {
::shared_ptr<terminal>
lists::marker::bind(const query_options& options) {
const auto& value = options.get_value_at(_bind_index);
auto& ltype = dynamic_cast<const list_type_impl&>(_receiver->type->without_reversed());
auto& ltype = static_cast<const list_type_impl&>(*_receiver->type);
if (value.is_null()) {
return nullptr;
} else if (value.is_unset_value()) {
return constants::UNSET_VALUE;
} else {
try {
ltype.validate(*value, options.get_cql_serialization_format());
return make_shared<lists::value>(value::from_serialized(*value, ltype, options.get_cql_serialization_format()));
return with_linearized(*value, [&] (bytes_view v) {
ltype.validate(v, options.get_cql_serialization_format());
return make_shared<lists::value>(value::from_serialized(v, ltype, options.get_cql_serialization_format()));
});
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what()));
@@ -299,7 +308,9 @@ lists::setter_by_index::execute(mutation& m, const clustering_key_prefix& prefix
return;
}
auto idx = value_cast<int32_t>(data_type_for<int32_t>()->deserialize(*index));
auto idx = with_linearized(*index, [] (bytes_view v) {
return value_cast<int32_t>(data_type_for<int32_t>()->deserialize(v));
});
auto&& existing_list_opt = params.get_prefetched_list(m.key(), prefix, column);
if (!existing_list_opt) {
throw exceptions::invalid_request_exception("Attempted to set an element on a list which is null");

View File

@@ -73,6 +73,7 @@ public:
};
class value : public multi_item_terminal, collection_terminal {
static value from_serialized(bytes_view v, const list_type_impl& type, cql_serialization_format sf);
public:
std::vector<bytes_opt> _elements;
public:

View File

@@ -55,14 +55,14 @@ lw_shared_ptr<column_specification>
maps::key_spec_of(const column_specification& column) {
return make_lw_shared<column_specification>(column.ks_name, column.cf_name,
::make_shared<column_identifier>(format("key({})", *column.name), true),
dynamic_cast<const map_type_impl&>(column.type->without_reversed()).get_keys_type());
dynamic_pointer_cast<const map_type_impl>(column.type)->get_keys_type());
}
lw_shared_ptr<column_specification>
maps::value_spec_of(const column_specification& column) {
return make_lw_shared<column_specification>(column.ks_name, column.cf_name,
::make_shared<column_identifier>(format("value({})", *column.name), true),
dynamic_cast<const map_type_impl&>(column.type->without_reversed()).get_values_type());
dynamic_pointer_cast<const map_type_impl>(column.type)->get_values_type());
}
::shared_ptr<term>
@@ -88,9 +88,7 @@ maps::literal::prepare(database& db, const sstring& keyspace, lw_shared_ptr<colu
values.emplace(k, v);
}
delayed_value value(
dynamic_cast<const map_type_impl&>(receiver->type->without_reversed()).get_keys_type()->as_less_comparator(),
values);
delayed_value value(static_pointer_cast<const map_type_impl>(receiver->type)->get_keys_type()->as_less_comparator(), values);
if (all_terminal) {
return value.bind(query_options::DEFAULT);
} else {
@@ -100,7 +98,7 @@ maps::literal::prepare(database& db, const sstring& keyspace, lw_shared_ptr<colu
void
maps::literal::validate_assignable_to(database& db, const sstring& keyspace, const column_specification& receiver) const {
if (!receiver.type->without_reversed().is_map()) {
if (!dynamic_pointer_cast<const map_type_impl>(receiver.type)) {
throw exceptions::invalid_request_exception(format("Invalid map literal for {} of type {}", *receiver.name, receiver.type->as_cql3_type()));
}
auto&& key_spec = maps::key_spec_of(receiver);
@@ -160,13 +158,15 @@ maps::value::from_serialized(const fragmented_temporary_buffer::view& fragmented
// Collections have this small hack that validate cannot be called on a serialized object,
// but compose does the validation (so we're fine).
// FIXME: deserialize_for_native_protocol?!
auto m = value_cast<map_type_impl::native_type>(type.deserialize(fragmented_value, sf));
return with_linearized(fragmented_value, [&] (bytes_view value) {
auto m = value_cast<map_type_impl::native_type>(type.deserialize(value, sf));
std::map<bytes, bytes, serialized_compare> map(type.get_keys_type()->as_less_comparator());
for (auto&& e : m) {
map.emplace(type.get_keys_type()->decompose(e.first),
type.get_values_type()->decompose(e.second));
}
return maps::value { std::move(map) };
});
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(e.what());
}
@@ -263,16 +263,14 @@ maps::marker::bind(const query_options& options) {
return constants::UNSET_VALUE;
}
try {
_receiver->type->validate(*val, options.get_cql_serialization_format());
with_linearized(*val, [&] (bytes_view value) {
_receiver->type->validate(value, options.get_cql_serialization_format());
});
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what()));
}
return ::make_shared<maps::value>(
maps::value::from_serialized(
*val,
dynamic_cast<const map_type_impl&>(_receiver->type->without_reversed()),
options.get_cql_serialization_format()));
return ::make_shared<maps::value>(maps::value::from_serialized(*val, static_cast<const map_type_impl&>(*_receiver->type), options.get_cql_serialization_format()));
}
void
@@ -307,12 +305,6 @@ maps::setter_by_key::execute(mutation& m, const clustering_key_prefix& prefix, c
assert(column.type->is_multi_cell()); // "Attempted to set a value for a single key on a frozen map"m
auto key = _k->bind_and_get(params._options);
auto value = _t->bind_and_get(params._options);
if (value.is_unset_value()) {
return;
}
if (key.is_unset_value() || value.is_unset_value()) {
throw invalid_request_exception("Invalid unset map key");
}
if (!key) {
throw invalid_request_exception("Invalid null map key");
}

View File

@@ -42,22 +42,19 @@
#include "cql3/cql_config.hh"
#include "query_options.hh"
#include "version.hh"
#include "db/consistency_level_type.hh"
namespace cql3 {
const cql_config default_cql_config;
thread_local const query_options::specific_options query_options::specific_options::DEFAULT{
-1, {}, db::consistency_level::SERIAL, api::missing_timestamp};
thread_local const query_options::specific_options query_options::specific_options::DEFAULT{-1, {}, {}, api::missing_timestamp};
thread_local query_options query_options::DEFAULT{default_cql_config,
db::consistency_level::ONE, infinite_timeout_config, std::nullopt,
db::consistency_level::ONE, std::nullopt,
std::vector<cql3::raw_value_view>(), false, query_options::specific_options::DEFAULT, cql_serialization_format::latest()};
query_options::query_options(const cql_config& cfg,
db::consistency_level consistency,
const ::timeout_config& timeout_config,
std::optional<std::vector<sstring_view>> names,
std::vector<cql3::raw_value> values,
std::vector<cql3::raw_value_view> value_views,
@@ -66,7 +63,6 @@ query_options::query_options(const cql_config& cfg,
cql_serialization_format sf)
: _cql_config(cfg)
, _consistency(consistency)
, _timeout_config(timeout_config)
, _names(std::move(names))
, _values(std::move(values))
, _value_views(value_views)
@@ -78,7 +74,6 @@ query_options::query_options(const cql_config& cfg,
query_options::query_options(const cql_config& cfg,
db::consistency_level consistency,
const ::timeout_config& timeout_config,
std::optional<std::vector<sstring_view>> names,
std::vector<cql3::raw_value> values,
bool skip_metadata,
@@ -86,7 +81,6 @@ query_options::query_options(const cql_config& cfg,
cql_serialization_format sf)
: _cql_config(cfg)
, _consistency(consistency)
, _timeout_config(timeout_config)
, _names(std::move(names))
, _values(std::move(values))
, _value_views()
@@ -99,7 +93,6 @@ query_options::query_options(const cql_config& cfg,
query_options::query_options(const cql_config& cfg,
db::consistency_level consistency,
const ::timeout_config& timeout_config,
std::optional<std::vector<sstring_view>> names,
std::vector<cql3::raw_value_view> value_views,
bool skip_metadata,
@@ -107,7 +100,6 @@ query_options::query_options(const cql_config& cfg,
cql_serialization_format sf)
: _cql_config(cfg)
, _consistency(consistency)
, _timeout_config(timeout_config)
, _names(std::move(names))
, _values()
, _value_views(std::move(value_views))
@@ -117,12 +109,11 @@ query_options::query_options(const cql_config& cfg,
{
}
query_options::query_options(db::consistency_level cl, const ::timeout_config& timeout_config, std::vector<cql3::raw_value> values,
query_options::query_options(db::consistency_level cl, std::vector<cql3::raw_value> values,
specific_options options)
: query_options(
default_cql_config,
cl,
timeout_config,
{},
std::move(values),
false,
@@ -135,7 +126,6 @@ query_options::query_options(db::consistency_level cl, const ::timeout_config& t
query_options::query_options(std::unique_ptr<query_options> qo, lw_shared_ptr<service::pager::paging_state> paging_state)
: query_options(qo->_cql_config,
qo->_consistency,
qo->get_timeout_config(),
std::move(qo->_names),
std::move(qo->_values),
std::move(qo->_value_views),
@@ -148,7 +138,6 @@ query_options::query_options(std::unique_ptr<query_options> qo, lw_shared_ptr<se
query_options::query_options(std::unique_ptr<query_options> qo, lw_shared_ptr<service::pager::paging_state> paging_state, int32_t page_size)
: query_options(qo->_cql_config,
qo->_consistency,
qo->get_timeout_config(),
std::move(qo->_names),
std::move(qo->_values),
std::move(qo->_value_views),
@@ -160,7 +149,7 @@ query_options::query_options(std::unique_ptr<query_options> qo, lw_shared_ptr<se
query_options::query_options(std::vector<cql3::raw_value> values)
: query_options(
db::consistency_level::ONE, infinite_timeout_config, std::move(values))
db::consistency_level::ONE, std::move(values))
{}
void query_options::prepare(const std::vector<lw_shared_ptr<column_specification>>& specs)

View File

@@ -51,7 +51,6 @@
#include "cql3/column_identifier.hh"
#include "cql3/values.hh"
#include "cql_serialization_format.hh"
#include "timeout_config.hh"
namespace cql3 {
@@ -75,7 +74,6 @@ public:
private:
const cql_config& _cql_config;
const db::consistency_level _consistency;
const timeout_config& _timeout_config;
const std::optional<std::vector<sstring_view>> _names;
std::vector<cql3::raw_value> _values;
std::vector<cql3::raw_value_view> _value_views;
@@ -109,7 +107,6 @@ public:
explicit query_options(const cql_config& cfg,
db::consistency_level consistency,
const timeout_config& timeouts,
std::optional<std::vector<sstring_view>> names,
std::vector<cql3::raw_value> values,
bool skip_metadata,
@@ -117,7 +114,6 @@ public:
cql_serialization_format sf);
explicit query_options(const cql_config& cfg,
db::consistency_level consistency,
const timeout_config& timeouts,
std::optional<std::vector<sstring_view>> names,
std::vector<cql3::raw_value> values,
std::vector<cql3::raw_value_view> value_views,
@@ -126,7 +122,6 @@ public:
cql_serialization_format sf);
explicit query_options(const cql_config& cfg,
db::consistency_level consistency,
const timeout_config& timeouts,
std::optional<std::vector<sstring_view>> names,
std::vector<cql3::raw_value_view> value_views,
bool skip_metadata,
@@ -158,13 +153,10 @@ public:
// forInternalUse
explicit query_options(std::vector<cql3::raw_value> values);
explicit query_options(db::consistency_level, const timeout_config& timeouts,
std::vector<cql3::raw_value> values, specific_options options = specific_options::DEFAULT);
explicit query_options(db::consistency_level, std::vector<cql3::raw_value> values, specific_options options = specific_options::DEFAULT);
explicit query_options(std::unique_ptr<query_options>, lw_shared_ptr<service::pager::paging_state> paging_state);
explicit query_options(std::unique_ptr<query_options>, lw_shared_ptr<service::pager::paging_state> paging_state, int32_t page_size);
const timeout_config& get_timeout_config() const { return _timeout_config; }
db::consistency_level get_consistency() const {
return _consistency;
}
@@ -258,7 +250,7 @@ query_options::query_options(query_options&& o, std::vector<OneMutationDataRange
std::vector<query_options> tmp;
tmp.reserve(values_ranges.size());
std::transform(values_ranges.begin(), values_ranges.end(), std::back_inserter(tmp), [this](auto& values_range) {
return query_options(_cql_config, _consistency, _timeout_config, {}, std::move(values_range), _skip_metadata, _options, _cql_serialization_format);
return query_options(_cql_config, _consistency, {}, std::move(values_range), _skip_metadata, _options, _cql_serialization_format);
});
_batch_options = std::move(tmp);
}

View File

@@ -619,7 +619,6 @@ query_options query_processor::make_internal_options(
const statements::prepared_statement::checked_weak_ptr& p,
const std::initializer_list<data_value>& values,
db::consistency_level cl,
const timeout_config& timeout_config,
int32_t page_size) const {
if (p->bound_names.size() != values.size()) {
throw std::invalid_argument(
@@ -643,11 +642,10 @@ query_options query_processor::make_internal_options(
api::timestamp_type ts = api::missing_timestamp;
return query_options(
cl,
timeout_config,
bound_values,
cql3::query_options::specific_options{page_size, std::move(paging_state), serial_consistency, ts});
}
return query_options(cl, timeout_config, bound_values);
return query_options(cl, bound_values);
}
statements::prepared_statement::checked_weak_ptr query_processor::prepare_internal(const sstring& query_string) {
@@ -671,7 +669,7 @@ struct internal_query_state {
::shared_ptr<internal_query_state> query_processor::create_paged_state(const sstring& query_string,
const std::initializer_list<data_value>& values, int32_t page_size) {
auto p = prepare_internal(query_string);
auto opts = make_internal_options(p, values, db::consistency_level::ONE, infinite_timeout_config, page_size);
auto opts = make_internal_options(p, values, db::consistency_level::ONE, page_size);
::shared_ptr<internal_query_state> res = ::make_shared<internal_query_state>(
internal_query_state{
query_string,
@@ -789,7 +787,16 @@ future<::shared_ptr<untyped_result_set>>
query_processor::execute_internal(
const sstring& query_string,
db::consistency_level cl,
const timeout_config& timeout_config,
const std::initializer_list<data_value>& values,
bool cache) {
return execute_internal(query_string, cl, *_internal_state, values, cache);
}
future<::shared_ptr<untyped_result_set>>
query_processor::execute_internal(
const sstring& query_string,
db::consistency_level cl,
service::query_state& query_state,
const std::initializer_list<data_value>& values,
bool cache) {
@@ -797,13 +804,13 @@ query_processor::execute_internal(
log.trace("execute_internal: {}\"{}\" ({})", cache ? "(cached) " : "", query_string, ::join(", ", values));
}
if (cache) {
return execute_with_params(prepare_internal(query_string), cl, timeout_config, values);
return execute_with_params(prepare_internal(query_string), cl, query_state, values);
} else {
auto p = parse_statement(query_string)->prepare(_db, _cql_stats);
p->statement->raw_cql_statement = query_string;
p->statement->validate(_proxy, *_internal_state);
auto checked_weak_ptr = p->checked_weak_from_this();
return execute_with_params(std::move(checked_weak_ptr), cl, timeout_config, values).finally([p = std::move(p)] {});
return execute_with_params(std::move(checked_weak_ptr), cl, query_state, values).finally([p = std::move(p)] {});
}
}
@@ -811,11 +818,11 @@ future<::shared_ptr<untyped_result_set>>
query_processor::execute_with_params(
statements::prepared_statement::checked_weak_ptr p,
db::consistency_level cl,
const timeout_config& timeout_config,
service::query_state& query_state,
const std::initializer_list<data_value>& values) {
auto opts = make_internal_options(p, values, cl, timeout_config);
return do_with(std::move(opts), [this, p = std::move(p)](auto & opts) {
return p->statement->execute(_proxy, *_internal_state, opts).then([](auto msg) {
auto opts = make_internal_options(p, values, cl);
return do_with(std::move(opts), [this, &query_state, p = std::move(p)](auto & opts) {
return p->statement->execute(_proxy, query_state, opts).then([](auto msg) {
return make_ready_future<::shared_ptr<untyped_result_set>>(::make_shared<untyped_result_set>(msg));
});
});

View File

@@ -215,8 +215,7 @@ public:
// creating namespaces, etc) is explicitly forbidden via this interface.
future<::shared_ptr<untyped_result_set>>
execute_internal(const sstring& query_string, const std::initializer_list<data_value>& values = { }) {
return execute_internal(query_string, db::consistency_level::ONE,
infinite_timeout_config, values, true);
return execute_internal(query_string, db::consistency_level::ONE, values, true);
}
statements::prepared_statement::checked_weak_ptr prepare_internal(const sstring& query);
@@ -305,14 +304,19 @@ public:
future<::shared_ptr<untyped_result_set>> execute_internal(
const sstring& query_string,
db::consistency_level,
const timeout_config& timeout_config,
const std::initializer_list<data_value>& = { },
bool cache = false);
future<::shared_ptr<untyped_result_set>> execute_internal(
const sstring& query_string,
db::consistency_level,
service::query_state& query_state,
const std::initializer_list<data_value>& = { },
bool cache = false);
future<::shared_ptr<untyped_result_set>> execute_with_params(
statements::prepared_statement::checked_weak_ptr p,
db::consistency_level,
const timeout_config& timeout_config,
service::query_state& query_state,
const std::initializer_list<data_value>& = { });
future<::shared_ptr<cql_transport::messages::result_message::prepared>>
@@ -341,7 +345,6 @@ private:
const statements::prepared_statement::checked_weak_ptr& p,
const std::initializer_list<data_value>&,
db::consistency_level,
const timeout_config& timeout_config,
int32_t page_size = -1) const;
future<::shared_ptr<cql_transport::messages::result_message>>

View File

@@ -171,7 +171,8 @@ public:
virtual void merge_with(::shared_ptr<restriction> restriction) override {
if (find_atom(restriction->expression, [] (const expr::binary_operator& b) {
return std::holds_alternative<std::vector<expr::column_value>>(b.lhs);
return std::holds_alternative<std::vector<expr::column_value>>(b.lhs)
&& std::get<std::vector<expr::column_value>>(b.lhs).size() > 1;
})) {
throw exceptions::invalid_request_exception(
"Mixing single column relations and multi column relations on clustering columns is not allowed");
@@ -212,22 +213,30 @@ private:
std::vector<range_type> compute_bounds(const query_options& options) const {
std::vector<range_type> ranges;
static constexpr auto invalid_null_msg = std::is_same<ValueType, partition_key>::value
? "Invalid null value for partition key part %s" : "Invalid null value for clustering key part %s";
// TODO: rewrite this to simply invoke possible_lhs_values on each clustering column, find the first
// non-list, and take Cartesian product of that prefix. No need for to_range() and std::get() here.
if (_restrictions->is_all_eq()) {
if (_restrictions->size() == 1) {
auto&& e = *restrictions().begin();
const auto b = std::get<expr::binary_operator>(e.second->expression).rhs->bind_and_get(options);
if (!b) {
throw exceptions::invalid_request_exception(sprint(invalid_null_msg, e.first->name_as_text()));
}
return {range_type::make_singular(ValueType::from_single_value(*_schema, to_bytes(b)))};
}
std::vector<bytes> components;
components.reserve(_restrictions->size());
for (auto&& e : restrictions()) {
const column_definition* def = e.first;
assert(components.size() == _schema->position(*def));
// Because _restrictions is all EQ, possible_lhs_values must return a list, not a range.
const auto b = std::get<expr::value_list>(possible_lhs_values(e.first, e.second->expression, options));
// Furthermore, this list is either a single element (when all RHSs are the same) or empty (when at
// least two are different, so the restrictions cannot hold simultaneously -- ie, c=1 AND c=2).
if (b.empty()) {
return {};
const auto b = std::get<expr::binary_operator>(e.second->expression).rhs->bind_and_get(options);
if (!b) {
throw exceptions::invalid_request_exception(sprint(invalid_null_msg, e.first->name_as_text()));
}
components.emplace_back(b.front());
components.emplace_back(to_bytes(b));
}
return {range_type::make_singular(ValueType::from_exploded(*_schema, std::move(components)))};
}
@@ -315,7 +324,7 @@ public:
std::vector<bytes_opt> res;
for (const ValueType& r : src) {
for (const auto& component : r.components()) {
res.emplace_back(to_bytes(component));
res.emplace_back(component);
}
}
return res;

View File

@@ -108,9 +108,6 @@ public:
return bytes_opt{};
} else {
const auto values = std::get<expr::value_list>(possible_lhs_values(&cdef, it->second->expression, options));
if (values.empty()) {
return bytes_opt{};
}
assert(values.size() == 1);
return values.front();
}
@@ -122,7 +119,7 @@ public:
* @param column_def the column definition
* @return the restriction associated to the specified column
*/
::shared_ptr<single_column_restriction> get_restriction(const column_definition& column_def) const {
::shared_ptr<restriction> get_restriction(const column_definition& column_def) const {
auto i = _restrictions.find(&column_def);
if (i == _restrictions.end()) {
return {};

View File

@@ -147,6 +147,7 @@ statement_restrictions::statement_restrictions(database& db,
const std::vector<::shared_ptr<relation>>& where_clause,
variable_specifications& bound_names,
bool selects_only_static_columns,
bool select_a_collection,
bool for_view,
bool allow_filtering)
: statement_restrictions(schema, allow_filtering)
@@ -226,11 +227,10 @@ statement_restrictions::statement_restrictions(database& db,
}
}
process_clustering_columns_restrictions(for_view, allow_filtering);
process_clustering_columns_restrictions(select_a_collection, for_view, allow_filtering);
// Covers indexes on the first clustering column (among others).
if (_is_key_range && _has_queriable_ck_index &&
!dynamic_pointer_cast<multi_column_restriction>(_clustering_columns_restrictions)) {
if (_is_key_range && _has_queriable_ck_index) {
_uses_secondary_indexing = true;
}
@@ -329,39 +329,20 @@ int statement_restrictions::score(const secondary_index::index& index) const {
return 1;
}
namespace {
using namespace cql3::restrictions;
/// If rs contains a restrictions_map of individual columns to their restrictions, returns it. Otherwise, returns null.
const single_column_restrictions::restrictions_map* get_individual_restrictions_map(const restrictions* rs) {
if (auto regular = dynamic_cast<const single_column_restrictions*>(rs)) {
return &regular->restrictions();
} else if (auto partition = dynamic_cast<const single_column_partition_key_restrictions*>(rs)) {
return &partition->restrictions();
} else if (auto clustering = dynamic_cast<const single_column_clustering_key_restrictions*>(rs)) {
return &clustering->restrictions();
}
return nullptr;
}
} // anonymous namespace
std::pair<std::optional<secondary_index::index>, ::shared_ptr<cql3::restrictions::restrictions>> statement_restrictions::find_idx(secondary_index::secondary_index_manager& sim) const {
std::optional<secondary_index::index> chosen_index;
int chosen_index_score = 0;
::shared_ptr<cql3::restrictions::restrictions> chosen_index_restrictions;
for (const auto& index : sim.list_indexes()) {
auto cdef = _schema->get_column_definition(to_bytes(index.target_column()));
for (::shared_ptr<cql3::restrictions::restrictions> restriction : index_restrictions()) {
if (auto rmap = get_individual_restrictions_map(restriction.get())) {
const auto found = rmap->find(cdef);
if (found != rmap->end() && is_supported_by(found->second->expression, index)
&& score(index) > chosen_index_score) {
chosen_index = index;
chosen_index_score = score(index);
chosen_index_restrictions = restriction;
for (const auto& cdef : restriction->get_column_defs()) {
if (index.depends_on(*cdef)) {
if (score(index) > chosen_index_score) {
chosen_index = index;
chosen_index_score = score(index);
chosen_index_restrictions = restriction;
}
}
}
}
@@ -454,11 +435,15 @@ bool statement_restrictions::has_unrestricted_clustering_columns() const {
return _clustering_columns_restrictions->has_unrestricted_components(*_schema);
}
void statement_restrictions::process_clustering_columns_restrictions(bool for_view, bool allow_filtering) {
void statement_restrictions::process_clustering_columns_restrictions(bool select_a_collection, bool for_view, bool allow_filtering) {
if (!has_clustering_columns_restriction()) {
return;
}
if (clustering_key_restrictions_has_IN() && select_a_collection) {
throw exceptions::invalid_request_exception(
"Cannot restrict clustering columns by IN relations when a collection is selected by the query");
}
if (find_atom(_clustering_columns_restrictions->expression, expr::is_on_collection)
&& !_has_queriable_ck_index && !allow_filtering) {
throw exceptions::invalid_request_exception(

View File

@@ -119,6 +119,7 @@ public:
const std::vector<::shared_ptr<relation>>& where_clause,
variable_specifications& bound_names,
bool selects_only_static_columns,
bool select_a_collection,
bool for_view = false,
bool allow_filtering = false);
@@ -216,9 +217,10 @@ private:
* Processes the clustering column restrictions.
*
* @param has_queriable_index <code>true</code> if some of the queried data are indexed, <code>false</code> otherwise
* @param select_a_collection <code>true</code> if the query should return a collection column
* @throws InvalidRequestException if the request is invalid
*/
void process_clustering_columns_restrictions(bool for_view, bool allow_filtering);
void process_clustering_columns_restrictions(bool select_a_collection, bool for_view, bool allow_filtering);
/**
* Returns the <code>Restrictions</code> for the specified type of columns.

View File

@@ -140,6 +140,21 @@ public:
return true;
}
/**
* Checks if this selection contains a collection.
*
* @return <code>true</code> if this selection contains a collection, <code>false</code> otherwise.
*/
bool contains_a_collection() const {
if (!_schema->has_multi_cell_collections()) {
return false;
}
return std::any_of(_columns.begin(), _columns.end(), [] (auto&& def) {
return def->type->is_collection() && def->type->is_multi_cell();
});
}
/**
* Returns the index of the specified column.
*

View File

@@ -31,7 +31,7 @@ lw_shared_ptr<column_specification>
sets::value_spec_of(const column_specification& column) {
return make_lw_shared<column_specification>(column.ks_name, column.cf_name,
::make_shared<column_identifier>(format("value({})", *column.name), true),
dynamic_cast<const set_type_impl&>(column.type->without_reversed()).get_elements_type());
dynamic_pointer_cast<const set_type_impl>(column.type)->get_elements_type());
}
shared_ptr<term>
@@ -74,8 +74,7 @@ sets::literal::prepare(database& db, const sstring& keyspace, lw_shared_ptr<colu
values.push_back(std::move(t));
}
auto compare = dynamic_cast<const set_type_impl&>(receiver->type->without_reversed())
.get_elements_type()->as_less_comparator();
auto compare = dynamic_pointer_cast<const set_type_impl>(receiver->type)->get_elements_type()->as_less_comparator();
auto value = ::make_shared<delayed_value>(compare, std::move(values));
if (all_terminal) {
@@ -87,7 +86,7 @@ sets::literal::prepare(database& db, const sstring& keyspace, lw_shared_ptr<colu
void
sets::literal::validate_assignable_to(database& db, const sstring& keyspace, const column_specification& receiver) const {
if (!receiver.type->without_reversed().is_set()) {
if (!dynamic_pointer_cast<const set_type_impl>(receiver.type)) {
// We've parsed empty maps as a set literal to break the ambiguity so
// handle that case now
if (dynamic_pointer_cast<const map_type_impl>(receiver.type) && _elements.empty()) {
@@ -107,7 +106,7 @@ sets::literal::validate_assignable_to(database& db, const sstring& keyspace, con
assignment_testable::test_result
sets::literal::test_assignment(database& db, const sstring& keyspace, const column_specification& receiver) const {
if (!receiver.type->without_reversed().is_set()) {
if (!dynamic_pointer_cast<const set_type_impl>(receiver.type)) {
// We've parsed empty maps as a set literal to break the ambiguity so handle that case now
if (dynamic_pointer_cast<const map_type_impl>(receiver.type) && _elements.empty()) {
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
@@ -138,12 +137,14 @@ sets::value::from_serialized(const fragmented_temporary_buffer::view& val, const
// Collections have this small hack that validate cannot be called on a serialized object,
// but compose does the validation (so we're fine).
// FIXME: deserializeForNativeProtocol?!
auto s = value_cast<set_type_impl::native_type>(type.deserialize(val, sf));
return with_linearized(val, [&] (bytes_view v) {
auto s = value_cast<set_type_impl::native_type>(type.deserialize(v, sf));
std::set<bytes, serialized_compare> elements(type.get_elements_type()->as_less_comparator());
for (auto&& element : s) {
elements.insert(elements.end(), type.get_elements_type()->decompose(element));
}
return value(std::move(elements));
});
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(e.what());
}
@@ -225,11 +226,8 @@ sets::delayed_value::bind(const query_options& options) {
sets::marker::marker(int32_t bind_index, lw_shared_ptr<column_specification> receiver)
: abstract_marker{bind_index, std::move(receiver)} {
if (!_receiver->type->without_reversed().is_set()) {
throw std::runtime_error(format("Receiver {} for set marker has wrong type: {}",
_receiver->cf_name, _receiver->type->name()));
assert(dynamic_cast<const set_type_impl*>(_receiver->type.get()));
}
}
::shared_ptr<terminal>
sets::marker::bind(const query_options& options) {
@@ -239,9 +237,11 @@ sets::marker::bind(const query_options& options) {
} else if (value.is_unset_value()) {
return constants::UNSET_VALUE;
} else {
auto& type = dynamic_cast<const set_type_impl&>(_receiver->type->without_reversed());
auto& type = static_cast<const set_type_impl&>(*_receiver->type);
try {
type.validate(*value, options.get_cql_serialization_format());
with_linearized(*value, [&] (bytes_view v) {
type.validate(v, options.get_cql_serialization_format());
});
} catch (marshal_exception& e) {
throw exceptions::invalid_request_exception(
format("Exception while binding column {:s}: {:s}", _receiver->name->to_cql_string(), e.what()));
@@ -284,7 +284,8 @@ void
sets::adder::do_add(mutation& m, const clustering_key_prefix& row_key, const update_parameters& params,
shared_ptr<term> value, const column_definition& column) {
auto set_value = dynamic_pointer_cast<sets::value>(std::move(value));
auto& set_type = dynamic_cast<const set_type_impl&>(column.type->without_reversed());
auto set_type = dynamic_cast<const set_type_impl*>(column.type.get());
assert(set_type);
if (column.type->is_multi_cell()) {
if (!set_value || set_value->_elements.empty()) {
return;
@@ -294,10 +295,10 @@ sets::adder::do_add(mutation& m, const clustering_key_prefix& row_key, const upd
collection_mutation_description mut;
for (auto&& e : set_value->_elements) {
mut.cells.emplace_back(e, params.make_cell(*set_type.value_comparator(), bytes_view(), atomic_cell::collection_member::yes));
mut.cells.emplace_back(e, params.make_cell(*set_type->value_comparator(), bytes_view(), atomic_cell::collection_member::yes));
}
m.set_cell(row_key, column, mut.serialize(set_type));
m.set_cell(row_key, column, mut.serialize(*set_type));
} else if (set_value != nullptr) {
// for frozen sets, we're overwriting the whole cell
auto v = set_type_impl::serialize_partially_deserialized_form(
@@ -314,7 +315,7 @@ sets::discarder::execute(mutation& m, const clustering_key_prefix& row_key, cons
assert(column.type->is_multi_cell()); // "Attempted to remove items from a frozen set";
auto&& value = _t->bind(params._options);
if (!value || value == constants::UNSET_VALUE) {
if (!value) {
return;
}

View File

@@ -45,7 +45,7 @@
#include "db/system_keyspace.hh"
#include "database.hh"
bool is_system_keyspace(std::string_view keyspace);
bool is_system_keyspace(const sstring& keyspace);
cql3::statements::alter_keyspace_statement::alter_keyspace_statement(sstring name, ::shared_ptr<ks_prop_defs> attrs)
: _name(name)
@@ -91,10 +91,10 @@ void cql3::statements::alter_keyspace_statement::validate(service::storage_proxy
}
}
future<shared_ptr<cql_transport::event::schema_change>> cql3::statements::alter_keyspace_statement::announce_migration(service::storage_proxy& proxy) const {
future<shared_ptr<cql_transport::event::schema_change>> cql3::statements::alter_keyspace_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const {
auto old_ksm = proxy.get_db().local().find_keyspace(_name).metadata();
const auto& tm = *proxy.get_token_metadata_ptr();
return service::get_local_migration_manager().announce_keyspace_update(_attrs->as_ks_metadata_update(old_ksm, tm)).then([this] {
return service::get_local_migration_manager().announce_keyspace_update(_attrs->as_ks_metadata_update(old_ksm, tm), is_local_only).then([this] {
using namespace cql_transport;
return ::make_shared<event::schema_change>(
event::schema_change::change_type::UPDATED,

View File

@@ -61,7 +61,7 @@ public:
future<> check_access(service::storage_proxy& proxy, const service::client_state& state) const override;
void validate(service::storage_proxy& proxy, const service::client_state& state) const override;
future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
};

View File

@@ -71,7 +71,7 @@ alter_table_statement::alter_table_statement(shared_ptr<cf_name> name,
future<> alter_table_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const {
using cdt = auth::command_desc::type;
return state.has_column_family_access(proxy.local_db(), keyspace(), column_family(), auth::permission::ALTER,
return state.has_column_family_access(keyspace(), column_family(), auth::permission::ALTER,
_type == type::opts ? cdt::ALTER_WITH_OPTS : cdt::OTHER);
}
@@ -288,7 +288,7 @@ void alter_table_statement::drop_column(const schema& schema, const table& cf, s
}
}
future<shared_ptr<cql_transport::event::schema_change>> alter_table_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> alter_table_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
auto& db = proxy.get_db().local();
auto s = validation::validate_column_family(db, keyspace(), column_family());
@@ -396,7 +396,7 @@ future<shared_ptr<cql_transport::event::schema_change>> alter_table_statement::a
break;
}
return service::get_local_migration_manager().announce_column_family_update(cfm.build(), false, std::move(view_updates))
return service::get_local_migration_manager().announce_column_family_update(cfm.build(), false, std::move(view_updates), is_local_only)
.then([this] {
using namespace cql_transport;
return ::make_shared<event::schema_change>(

View File

@@ -80,7 +80,7 @@ public:
virtual future<> check_access(service::storage_proxy& proxy, const service::client_state& state) const override;
virtual void validate(service::storage_proxy& proxy, const service::client_state& state) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
private:
void add_column(const schema& schema, const table& cf, schema_builder& cfm, std::vector<view_ptr>& view_updates, const column_identifier& column_name, const cql3_type validator, const column_definition* def, bool is_static) const;

View File

@@ -78,7 +78,7 @@ const sstring& alter_type_statement::keyspace() const
return _name.get_keyspace();
}
void alter_type_statement::do_announce_migration(database& db, ::keyspace& ks) const
void alter_type_statement::do_announce_migration(database& db, ::keyspace& ks, bool is_local_only) const
{
auto&& all_types = ks.metadata()->user_types().get_all_types();
auto to_update = all_types.find(_name.get_user_type_name());
@@ -100,7 +100,7 @@ void alter_type_statement::do_announce_migration(database& db, ::keyspace& ks) c
// Now, we need to announce the type update to basically change it for new tables using this type,
// but we also need to find all existing user types and CF using it and change them.
service::get_local_migration_manager().announce_type_update(updated).get();
service::get_local_migration_manager().announce_type_update(updated, is_local_only).get();
for (auto&& schema : ks.metadata()->cf_meta_data() | boost::adaptors::map_values) {
auto cfm = schema_builder(schema);
@@ -115,21 +115,21 @@ void alter_type_statement::do_announce_migration(database& db, ::keyspace& ks) c
}
if (modified) {
if (schema->is_view()) {
service::get_local_migration_manager().announce_view_update(view_ptr(cfm.build())).get();
service::get_local_migration_manager().announce_view_update(view_ptr(cfm.build()), is_local_only).get();
} else {
service::get_local_migration_manager().announce_column_family_update(cfm.build(), false, {}).get();
service::get_local_migration_manager().announce_column_family_update(cfm.build(), false, {}, is_local_only).get();
}
}
}
}
future<shared_ptr<cql_transport::event::schema_change>> alter_type_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> alter_type_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
return seastar::async([this, &proxy] {
return seastar::async([this, &proxy, is_local_only] {
auto&& db = proxy.get_db().local();
try {
auto&& ks = db.find_keyspace(keyspace());
do_announce_migration(db, ks);
do_announce_migration(db, ks, is_local_only);
using namespace cql_transport;
return ::make_shared<event::schema_change>(
event::schema_change::change_type::UPDATED,

View File

@@ -63,14 +63,14 @@ public:
virtual const sstring& keyspace() const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
class add_or_alter;
class renames;
protected:
virtual user_type make_updated_type(database& db, user_type to_update) const = 0;
private:
void do_announce_migration(database& db, ::keyspace& ks) const;
void do_announce_migration(database& db, ::keyspace& ks, bool is_local_only) const;
};
class alter_type_statement::add_or_alter : public alter_type_statement {

View File

@@ -60,10 +60,9 @@ alter_view_statement::alter_view_statement(::shared_ptr<cf_name> view_name, ::sh
future<> alter_view_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const
{
try {
const database& db = proxy.local_db();
auto&& s = db.find_schema(keyspace(), column_family());
auto&& s = proxy.get_db().local().find_schema(keyspace(), column_family());
if (s->is_view()) {
return state.has_column_family_access(db, keyspace(), s->view_info()->base_name(), auth::permission::ALTER);
return state.has_column_family_access(keyspace(), s->view_info()->base_name(), auth::permission::ALTER);
}
} catch (const no_such_column_family& e) {
// Will be validated afterwards.
@@ -76,7 +75,7 @@ void alter_view_statement::validate(service::storage_proxy&, const service::clie
// validated in announce_migration()
}
future<shared_ptr<cql_transport::event::schema_change>> alter_view_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> alter_view_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
auto&& db = proxy.get_db().local();
schema_ptr schema = validation::validate_column_family(db, keyspace(), column_family());
@@ -108,7 +107,7 @@ future<shared_ptr<cql_transport::event::schema_change>> alter_view_statement::an
"the corresponding data in the parent table.");
}
return service::get_local_migration_manager().announce_view_update(view_ptr(builder.build())).then([this] {
return service::get_local_migration_manager().announce_view_update(view_ptr(builder.build()), is_local_only).then([this] {
using namespace cql_transport;
return ::make_shared<event::schema_change>(

View File

@@ -63,7 +63,7 @@ public:
virtual void validate(service::storage_proxy&, const service::client_state& state) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
};

View File

@@ -59,10 +59,6 @@ timeout_for_type(batch_statement::type t) {
: &timeout_config::write_timeout;
}
db::timeout_clock::duration batch_statement::get_timeout(const query_options& options) const {
return _attrs->is_timeout_set() ? _attrs->get_timeout(options) : options.get_timeout_config().*get_timeout_config_selector();
}
batch_statement::batch_statement(int bound_terms, type type_,
std::vector<single_statement> statements,
std::unique_ptr<attributes> attrs,
@@ -290,7 +286,7 @@ future<shared_ptr<cql_transport::messages::result_message>> batch_statement::do_
++_stats.batches;
_stats.statements_in_batches += _statements.size();
auto timeout = db::timeout_clock::now() + get_timeout(options);
auto timeout = db::timeout_clock::now() + query_state.get_client_state().get_timeout_config().*get_timeout_config_selector();
return get_mutations(storage, options, timeout, local, now, query_state).then([this, &storage, &options, timeout, tr_state = query_state.get_trace_state(),
permit = query_state.get_permit()] (std::vector<mutation> ms) mutable {
return execute_without_conditions(storage, std::move(ms), options.get_consistency(), timeout, std::move(tr_state), std::move(permit));
@@ -347,7 +343,7 @@ future<shared_ptr<cql_transport::messages::result_message>> batch_statement::exe
schema_ptr schema;
db::timeout_clock::time_point now = db::timeout_clock::now();
const timeout_config& cfg = options.get_timeout_config();
const timeout_config& cfg = qs.get_client_state().get_timeout_config();
auto batch_timeout = now + cfg.write_timeout; // Statement timeout.
auto cas_timeout = now + cfg.cas_timeout; // Ballot contention timeout.
auto read_timeout = now + cfg.read_timeout; // Query timeout.

View File

@@ -170,8 +170,6 @@ private:
service::storage_proxy& storage,
const query_options& options,
service::query_state& state) const;
db::timeout_clock::duration get_timeout(const query_options& options) const;
public:
// FIXME: no cql_statement::to_string() yet
#if 0

View File

@@ -157,7 +157,6 @@ void cf_prop_defs::validate(const database& db, const schema::extensions_map& sc
}
validate_minimum_int(KW_DEFAULT_TIME_TO_LIVE, 0, DEFAULT_DEFAULT_TIME_TO_LIVE);
validate_minimum_int(KW_PAXOSGRACESECONDS, 0, DEFAULT_GC_GRACE_SECONDS);
auto min_index_interval = get_int(KW_MIN_INDEX_INTERVAL, DEFAULT_MIN_INDEX_INTERVAL);
auto max_index_interval = get_int(KW_MAX_INDEX_INTERVAL, DEFAULT_MAX_INDEX_INTERVAL);

View File

@@ -59,11 +59,11 @@ std::unique_ptr<prepared_statement> create_function_statement::prepare(database&
}
future<shared_ptr<cql_transport::event::schema_change>> create_function_statement::announce_migration(
service::storage_proxy& proxy) const {
service::storage_proxy& proxy, bool is_local_only) const {
if (!_func) {
return make_ready_future<::shared_ptr<cql_transport::event::schema_change>>();
}
return service::get_local_migration_manager().announce_new_function(_func).then([this] {
return service::get_local_migration_manager().announce_new_function(_func, is_local_only).then([this] {
return create_schema_change(*_func, true);
});
}

View File

@@ -29,7 +29,7 @@ namespace statements {
class create_function_statement final : public create_function_statement_base {
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(
service::storage_proxy& proxy) const override;
service::storage_proxy& proxy, bool is_local_only) const override;
virtual void create(service::storage_proxy& proxy, functions::function* old) const override;
sstring _language;
sstring _body;

View File

@@ -73,7 +73,7 @@ create_index_statement::create_index_statement(::shared_ptr<cf_name> name,
future<>
create_index_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const {
return state.has_column_family_access(proxy.local_db(), keyspace(), column_family(), auth::permission::ALTER);
return state.has_column_family_access(keyspace(), column_family(), auth::permission::ALTER);
}
void
@@ -271,7 +271,7 @@ void create_index_statement::validate_targets_for_multi_column_index(std::vector
}
future<::shared_ptr<cql_transport::event::schema_change>>
create_index_statement::announce_migration(service::storage_proxy& proxy) const {
create_index_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const {
auto& db = proxy.get_db().local();
auto schema = db.find_schema(keyspace(), column_family());
std::vector<::shared_ptr<index_target>> targets;
@@ -310,7 +310,7 @@ create_index_statement::announce_migration(service::storage_proxy& proxy) const
schema_builder builder{schema};
builder.with_index(index);
return service::get_local_migration_manager().announce_column_family_update(
builder.build(), false, {}).then([this]() {
builder.build(), false, {}, is_local_only).then([this]() {
using namespace cql_transport;
return ::make_shared<event::schema_change>(
event::schema_change::change_type::UPDATED,

View File

@@ -79,7 +79,7 @@ public:
future<> check_access(service::storage_proxy& proxy, const service::client_state& state) const override;
void validate(service::storage_proxy&, const service::client_state& state) const override;
future<::shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy&) const override;
future<::shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy&, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
private:

View File

@@ -47,7 +47,7 @@
#include <regex>
bool is_system_keyspace(std::string_view keyspace);
bool is_system_keyspace(const sstring& keyspace);
namespace cql3 {
@@ -106,11 +106,11 @@ void create_keyspace_statement::validate(service::storage_proxy&, const service:
#endif
}
future<shared_ptr<cql_transport::event::schema_change>> create_keyspace_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> create_keyspace_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
return make_ready_future<>().then([this, p = proxy.shared_from_this()] {
return make_ready_future<>().then([this, p = proxy.shared_from_this(), is_local_only] {
const auto& tm = *p->get_token_metadata_ptr();
return service::get_local_migration_manager().announce_new_keyspace(_attrs->as_ks_metadata(_name, tm));
return service::get_local_migration_manager().announce_new_keyspace(_attrs->as_ks_metadata(_name, tm), is_local_only);
}).then_wrapped([this] (auto&& f) {
try {
f.get();

View File

@@ -84,7 +84,7 @@ public:
*/
virtual void validate(service::storage_proxy&, const service::client_state& state) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;

View File

@@ -97,10 +97,10 @@ std::vector<column_definition> create_table_statement::get_columns() const
return column_defs;
}
future<shared_ptr<cql_transport::event::schema_change>> create_table_statement::announce_migration(service::storage_proxy& proxy) const {
future<shared_ptr<cql_transport::event::schema_change>> create_table_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const {
auto schema = get_cf_meta_data(proxy.get_db().local());
return make_ready_future<>().then([this, schema = std::move(schema)] {
return service::get_local_migration_manager().announce_new_column_family(std::move(schema));
return make_ready_future<>().then([this, is_local_only, schema = std::move(schema)] {
return service::get_local_migration_manager().announce_new_column_family(std::move(schema), is_local_only);
}).then_wrapped([this] (auto&& f) {
try {
f.get();

View File

@@ -102,7 +102,7 @@ public:
virtual void validate(service::storage_proxy&, const service::client_state& state) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;

View File

@@ -138,7 +138,7 @@ inline user_type create_type_statement::create_type(database& db) const
std::move(field_names), std::move(field_types), true /* multi cell */);
}
future<shared_ptr<cql_transport::event::schema_change>> create_type_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> create_type_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
auto&& db = proxy.get_db().local();
@@ -152,7 +152,7 @@ future<shared_ptr<cql_transport::event::schema_change>> create_type_statement::a
auto type = create_type(db);
check_for_duplicate_names(type);
return service::get_local_migration_manager().announce_new_type(type).then([this] {
return service::get_local_migration_manager().announce_new_type(type, is_local_only).then([this] {
using namespace cql_transport;
return ::make_shared<event::schema_change>(

View File

@@ -65,7 +65,7 @@ public:
virtual const sstring& keyspace() const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;

View File

@@ -89,7 +89,7 @@ create_view_statement::create_view_statement(
}
future<> create_view_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const {
return state.has_column_family_access(proxy.local_db(), keyspace(), _base_name->get_column_family(), auth::permission::ALTER);
return state.has_column_family_access(keyspace(), _base_name->get_column_family(), auth::permission::ALTER);
}
void create_view_statement::validate(service::storage_proxy& proxy, const service::client_state& state) const {
@@ -140,7 +140,7 @@ static bool validate_primary_key(
return new_non_pk_column;
}
future<shared_ptr<cql_transport::event::schema_change>> create_view_statement::announce_migration(service::storage_proxy& proxy) const {
future<shared_ptr<cql_transport::event::schema_change>> create_view_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const {
// We need to make sure that:
// - primary key includes all columns in base table's primary key
// - make sure that the select statement does not have anything other than columns
@@ -225,7 +225,7 @@ future<shared_ptr<cql_transport::event::schema_change>> create_view_statement::a
}
auto parameters = make_lw_shared<raw::select_statement::parameters>(raw::select_statement::parameters::orderings_type(), false, true);
raw::select_statement raw_select(_base_name, std::move(parameters), _select_clause, _where_clause, nullptr, nullptr, {}, std::make_unique<cql3::attributes::raw>());
raw::select_statement raw_select(_base_name, std::move(parameters), _select_clause, _where_clause, nullptr, nullptr, {});
raw_select.prepare_keyspace(keyspace());
raw_select.set_bound_variables({});
@@ -350,8 +350,8 @@ future<shared_ptr<cql_transport::event::schema_change>> create_view_statement::a
auto where_clause_text = util::relations_to_where_clause(_where_clause);
builder.with_view_info(schema->id(), schema->cf_name(), included.empty(), std::move(where_clause_text));
return make_ready_future<>().then([definition = view_ptr(builder.build())]() mutable {
return service::get_local_migration_manager().announce_new_view(definition);
return make_ready_future<>().then([definition = view_ptr(builder.build()), is_local_only]() mutable {
return service::get_local_migration_manager().announce_new_view(definition, is_local_only);
}).then_wrapped([this] (auto&& f) {
try {
f.get();

View File

@@ -68,7 +68,7 @@ public:
// Functions we need to override to subclass schema_altering_statement
virtual future<> check_access(service::storage_proxy& proxy, const service::client_state& state) const override;
virtual void validate(service::storage_proxy&, const service::client_state& state) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
// FIXME: continue here. See create_table_statement.hh and CreateViewStatement.java

View File

@@ -33,7 +33,7 @@ std::unique_ptr<prepared_statement> drop_function_statement::prepare(database& d
}
future<shared_ptr<cql_transport::event::schema_change>> drop_function_statement::announce_migration(
service::storage_proxy& proxy) const {
service::storage_proxy& proxy, bool is_local_only) const {
if (!_func) {
return make_ready_future<shared_ptr<cql_transport::event::schema_change>>();
}
@@ -41,7 +41,7 @@ future<shared_ptr<cql_transport::event::schema_change>> drop_function_statement:
if (!user_func) {
throw exceptions::invalid_request_exception(format("'{}' is not a user defined function", _func));
}
return service::get_local_migration_manager().announce_function_drop(user_func).then([this] {
return service::get_local_migration_manager().announce_function_drop(user_func, is_local_only).then([this] {
return create_schema_change(*_func, false);
});
}

View File

@@ -28,7 +28,7 @@ namespace statements {
class drop_function_statement final : public drop_function_statement_base {
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(
service::storage_proxy& proxy) const override;
service::storage_proxy& proxy, bool is_local_only) const override;
public:
drop_function_statement(functions::function_name name, std::vector<shared_ptr<cql3_type::raw>> arg_types,

View File

@@ -70,7 +70,7 @@ future<> drop_index_statement::check_access(service::storage_proxy& proxy, const
if (!cfm) {
return make_ready_future<>();
}
return state.has_column_family_access(proxy.local_db(), cfm->ks_name(), cfm->cf_name(), auth::permission::ALTER);
return state.has_column_family_access(cfm->ks_name(), cfm->cf_name(), auth::permission::ALTER);
}
void drop_index_statement::validate(service::storage_proxy& proxy, const service::client_state& state) const
@@ -86,7 +86,7 @@ void drop_index_statement::validate(service::storage_proxy& proxy, const service
}
}
future<shared_ptr<cql_transport::event::schema_change>> drop_index_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> drop_index_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
auto cfm = lookup_indexed_table(proxy);
if (!cfm) {
@@ -95,7 +95,7 @@ future<shared_ptr<cql_transport::event::schema_change>> drop_index_statement::an
++_cql_stats->secondary_index_drops;
auto builder = schema_builder(cfm);
builder.without_index(_index_name);
return service::get_local_migration_manager().announce_column_family_update(builder.build(), false, {}).then([cfm] {
return service::get_local_migration_manager().announce_column_family_update(builder.build(), false, {}, is_local_only).then([cfm] {
// Dropping an index is akin to updating the CF
// Note that we shouldn't call columnFamily() at this point because the index has been dropped and the call to lookupIndexedTable()
// in that method would now throw.

View File

@@ -72,7 +72,7 @@ public:
virtual void validate(service::storage_proxy&, const service::client_state& state) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
private:

View File

@@ -74,10 +74,10 @@ const sstring& drop_keyspace_statement::keyspace() const
return _keyspace;
}
future<shared_ptr<cql_transport::event::schema_change>> drop_keyspace_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> drop_keyspace_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
return make_ready_future<>().then([this] {
return service::get_local_migration_manager().announce_keyspace_drop(_keyspace);
return make_ready_future<>().then([this, is_local_only] {
return service::get_local_migration_manager().announce_keyspace_drop(_keyspace, is_local_only);
}).then_wrapped([this] (auto&& f) {
try {
f.get();

View File

@@ -59,7 +59,7 @@ public:
virtual const sstring& keyspace() const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
};

View File

@@ -58,7 +58,7 @@ future<> drop_table_statement::check_access(service::storage_proxy& proxy, const
{
// invalid_request_exception is only thrown synchronously.
try {
return state.has_column_family_access(proxy.local_db(), keyspace(), column_family(), auth::permission::DROP);
return state.has_column_family_access(keyspace(), column_family(), auth::permission::DROP);
} catch (exceptions::invalid_request_exception&) {
if (!_if_exists) {
throw;
@@ -72,10 +72,10 @@ void drop_table_statement::validate(service::storage_proxy&, const service::clie
// validated in announce_migration()
}
future<shared_ptr<cql_transport::event::schema_change>> drop_table_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> drop_table_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
return make_ready_future<>().then([this] {
return service::get_local_migration_manager().announce_column_family_drop(keyspace(), column_family());
return make_ready_future<>().then([this, is_local_only] {
return service::get_local_migration_manager().announce_column_family_drop(keyspace(), column_family(), is_local_only);
}).then_wrapped([this] (auto&& f) {
try {
f.get();

View File

@@ -58,7 +58,7 @@ public:
virtual void validate(service::storage_proxy&, const service::client_state& state) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
};

View File

@@ -142,7 +142,7 @@ const sstring& drop_type_statement::keyspace() const
return _name.get_keyspace();
}
future<shared_ptr<cql_transport::event::schema_change>> drop_type_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> drop_type_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
auto&& db = proxy.get_db().local();
@@ -157,7 +157,7 @@ future<shared_ptr<cql_transport::event::schema_change>> drop_type_statement::ann
return make_ready_future<::shared_ptr<cql_transport::event::schema_change>>();
}
return service::get_local_migration_manager().announce_type_drop(to_drop->second).then([this] {
return service::get_local_migration_manager().announce_type_drop(to_drop->second, is_local_only).then([this] {
using namespace cql_transport;
return ::make_shared<event::schema_change>(

View File

@@ -61,7 +61,7 @@ public:
virtual const sstring& keyspace() const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
};

View File

@@ -58,10 +58,9 @@ drop_view_statement::drop_view_statement(::shared_ptr<cf_name> view_name, bool i
future<> drop_view_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const
{
try {
const database& db = proxy.local_db();
auto&& s = db.find_schema(keyspace(), column_family());
auto&& s = proxy.get_db().local().find_schema(keyspace(), column_family());
if (s->is_view()) {
return state.has_column_family_access(db, keyspace(), s->view_info()->base_name(), auth::permission::ALTER);
return state.has_column_family_access(keyspace(), s->view_info()->base_name(), auth::permission::ALTER);
}
} catch (const no_such_column_family& e) {
// Will be validated afterwards.
@@ -74,10 +73,10 @@ void drop_view_statement::validate(service::storage_proxy&, const service::clien
// validated in migration_manager::announce_view_drop()
}
future<shared_ptr<cql_transport::event::schema_change>> drop_view_statement::announce_migration(service::storage_proxy& proxy) const
future<shared_ptr<cql_transport::event::schema_change>> drop_view_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) const
{
return make_ready_future<>().then([this] {
return service::get_local_migration_manager().announce_view_drop(keyspace(), column_family());
return make_ready_future<>().then([this, is_local_only] {
return service::get_local_migration_manager().announce_view_drop(keyspace(), column_family(), is_local_only);
}).then_wrapped([this] (auto&& f) {
try {
f.get();

View File

@@ -63,7 +63,7 @@ public:
virtual void validate(service::storage_proxy&, const service::client_state& state) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const override;
virtual future<shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const override;
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override;
};

View File

@@ -59,7 +59,7 @@
#include "partition_slice_builder.hh"
#include "cas_request.hh"
bool is_system_keyspace(std::string_view name);
bool is_system_keyspace(const sstring& name);
namespace cql3 {
@@ -74,10 +74,6 @@ modification_statement_timeout(const schema& s) {
}
}
db::timeout_clock::duration modification_statement::get_timeout(const query_options& options) const {
return attrs->is_timeout_set() ? attrs->get_timeout(options) : options.get_timeout_config().*get_timeout_config_selector();
}
modification_statement::modification_statement(statement_type type_, uint32_t bound_terms, schema_ptr schema_, std::unique_ptr<attributes> attrs_, cql_stats& stats_)
: cql_statement_opt_metadata(modification_statement_timeout(*schema_))
, type{type_}
@@ -124,11 +120,10 @@ gc_clock::duration modification_statement::get_time_to_live(const query_options&
}
future<> modification_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const {
const database& db = proxy.local_db();
auto f = state.has_column_family_access(db, keyspace(), column_family(), auth::permission::MODIFY);
auto f = state.has_column_family_access(keyspace(), column_family(), auth::permission::MODIFY);
if (has_conditions()) {
f = f.then([this, &state, &db] {
return state.has_column_family_access(db, keyspace(), column_family(), auth::permission::SELECT);
f = f.then([this, &state] {
return state.has_column_family_access(keyspace(), column_family(), auth::permission::SELECT);
});
}
return f;
@@ -291,7 +286,7 @@ modification_statement::do_execute(service::storage_proxy& proxy, service::query
future<>
modification_statement::execute_without_condition(service::storage_proxy& proxy, service::query_state& qs, const query_options& options) const {
auto cl = options.get_consistency();
auto timeout = db::timeout_clock::now() + get_timeout(options);
auto timeout = db::timeout_clock::now() + qs.get_client_state().get_timeout_config().*get_timeout_config_selector();
return get_mutations(proxy, options, timeout, false, options.get_timestamp(qs), qs).then([this, cl, timeout, &proxy, &qs] (auto mutations) {
if (mutations.empty()) {
return now();
@@ -307,7 +302,7 @@ modification_statement::execute_with_condition(service::storage_proxy& proxy, se
auto cl_for_learn = options.get_consistency();
auto cl_for_paxos = options.check_serial_consistency();
db::timeout_clock::time_point now = db::timeout_clock::now();
const timeout_config& cfg = options.get_timeout_config();
const timeout_config& cfg = qs.get_client_state().get_timeout_config();
auto statement_timeout = now + cfg.write_timeout; // All CAS networking operations run with write timeout.
auto cas_timeout = now + cfg.cas_timeout; // When to give up due to contention.

View File

@@ -298,9 +298,6 @@ protected:
* @throws InvalidRequestException
*/
virtual void validate_where_clause_for_conditions() const;
db::timeout_clock::duration get_timeout(const query_options& options) const;
friend class raw::modification_statement;
};

View File

@@ -48,7 +48,6 @@
#include "cql3/selection/raw_selector.hh"
#include "cql3/restrictions/statement_restrictions.hh"
#include "cql3/result_set.hh"
#include "cql3/attributes.hh"
#include "exceptions/unrecognized_entity_exception.hh"
#include "service/client_state.hh"
#include <seastar/core/shared_ptr.hh>
@@ -106,7 +105,6 @@ private:
::shared_ptr<term::raw> _limit;
::shared_ptr<term::raw> _per_partition_limit;
std::vector<::shared_ptr<cql3::column_identifier::raw>> _group_by_columns;
std::unique_ptr<cql3::attributes::raw> _attrs;
public:
select_statement(::shared_ptr<cf_name> cf_name,
lw_shared_ptr<const parameters> parameters,
@@ -114,8 +112,7 @@ public:
std::vector<::shared_ptr<relation>> where_clause,
::shared_ptr<term::raw> limit,
::shared_ptr<term::raw> per_partition_limit,
std::vector<::shared_ptr<cql3::column_identifier::raw>> group_by_columns,
std::unique_ptr<cql3::attributes::raw> attrs);
std::vector<::shared_ptr<cql3::column_identifier::raw>> group_by_columns);
virtual std::unique_ptr<prepared_statement> prepare(database& db, cql_stats& stats) override {
return prepare(db, stats, false);

View File

@@ -59,6 +59,7 @@
#include "gms/feature_service.hh"
#include "transport/messages/result_message.hh"
#include "unimplemented.hh"
#include "concrete_types.hh"
namespace cql3 {
@@ -105,6 +106,30 @@ future<> create_role_statement::grant_permissions_to_creator(const service::clie
});
}
static void validate_timeout_options(const auth::authentication_options& auth_options) {
if (!auth_options.options) {
return;
}
const auto& options = *auth_options.options;
auto check_duration = [&] (const sstring& repr) {
data_value v = duration_type->deserialize(duration_type->from_string(repr));
cql_duration duration = static_pointer_cast<const duration_type_impl>(duration_type)->from_value(v);
if (duration.months || duration.days) {
throw exceptions::invalid_request_exception("Timeout values cannot be longer than 24h");
}
if (duration.nanoseconds % 1'000'000 != 0) {
throw exceptions::invalid_request_exception("Timeout values must be expressed in millisecond granularity");
}
};
for (auto opt : {"read_timeout", "write_timeout"}) {
auto it = options.find(opt);
if (it != options.end()) {
check_duration(it->second);
}
}
}
void create_role_statement::validate(service::storage_proxy& p, const service::client_state&) const {
validate_cluster_support(p);
}
@@ -137,9 +162,12 @@ create_role_statement::execute(service::storage_proxy&,
[this, &state](const auth::role_config& config, const auth::authentication_options& authen_options) {
const auto& cs = state.get_client_state();
auto& as = *cs.get_auth_service();
validate_timeout_options(authen_options);
return auth::create_role(as, _role, config, authen_options).then([this, &cs] {
return grant_permissions_to_creator(cs);
}).then([&state] () mutable {
return state.get_client_state().update_per_role_params();
}).then([] {
return void_result_message();
}).handle_exception_type([this](const auth::role_already_exists& e) {
@@ -224,8 +252,9 @@ alter_role_statement::execute(service::storage_proxy&, service::query_state& sta
extract_authentication_options(_options),
[this, &state](const auth::role_config_update& update, const auth::authentication_options& authen_options) {
auto& as = *state.get_client_state().get_auth_service();
return auth::alter_role(as, _role, update, authen_options).then([] {
return auth::alter_role(as, _role, update, authen_options).then([&state] () mutable {
return state.get_client_state().update_per_role_params();
}).then([] {
return void_result_message();
}).handle_exception_type([](const auth::nonexistant_role& e) {
return make_exception_future<result_message_ptr>(exceptions::invalid_request_exception(e.what()));

View File

@@ -90,10 +90,10 @@ void schema_altering_statement::prepare_keyspace(const service::client_state& st
}
future<::shared_ptr<messages::result_message>>
schema_altering_statement::execute0(service::storage_proxy& proxy, service::query_state& state, const query_options& options) const {
schema_altering_statement::execute0(service::storage_proxy& proxy, service::query_state& state, const query_options& options, bool is_local_only) const {
// If an IF [NOT] EXISTS clause was used, this may not result in an actual schema change. To avoid doing
// extra work in the drivers to handle schema changes, we return an empty message in this case. (CASSANDRA-7600)
return announce_migration(proxy).then([this] (auto ce) {
return announce_migration(proxy, is_local_only).then([this] (auto ce) {
::shared_ptr<messages::result_message> result;
if (!ce) {
result = ::make_shared<messages::result_message::void_message>();
@@ -120,7 +120,7 @@ schema_altering_statement::execute(service::storage_proxy& proxy, service::query
}
}
return execute0(proxy, state, options).then([this, &state, internal](::shared_ptr<messages::result_message> result) {
return execute0(proxy, state, options, internal).then([this, &state, internal](::shared_ptr<messages::result_message> result) {
auto permissions_granted_fut = internal
? make_ready_future<>()
: grant_permissions_to_creator(state.get_client_state());

View File

@@ -65,7 +65,7 @@ private:
const bool _is_column_family_level;
future<::shared_ptr<messages::result_message>>
execute0(service::storage_proxy& proxy, service::query_state& state, const query_options& options) const;
execute0(service::storage_proxy& proxy, service::query_state& state, const query_options& options, bool) const;
protected:
explicit schema_altering_statement(timeout_config_selector timeout_selector = &timeout_config::other_timeout);
@@ -87,7 +87,7 @@ protected:
virtual void prepare_keyspace(const service::client_state& state) override;
virtual future<::shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy) const = 0;
virtual future<::shared_ptr<cql_transport::event::schema_change>> announce_migration(service::storage_proxy& proxy, bool is_local_only) const = 0;
virtual future<::shared_ptr<messages::result_message>>
execute(service::storage_proxy& proxy, service::query_state& state, const query_options& options) const override;

View File

@@ -62,7 +62,7 @@
#include "test/lib/select_statement_utils.hh"
#include <boost/algorithm/cxx11/any_of.hpp>
bool is_system_keyspace(std::string_view name);
bool is_system_keyspace(const sstring& name);
namespace cql3 {
@@ -138,8 +138,7 @@ select_statement::select_statement(schema_ptr schema,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
cql_stats& stats,
std::unique_ptr<attributes> attrs)
cql_stats& stats)
: cql_statement(select_timeout(*restrictions))
, _schema(schema)
, _bound_terms(bound_terms)
@@ -153,7 +152,6 @@ select_statement::select_statement(schema_ptr schema,
, _ordering_comparator(std::move(ordering_comparator))
, _stats(stats)
, _ks_sel(::is_system_keyspace(schema->ks_name()) ? ks_selector::SYSTEM : ks_selector::NONSYSTEM)
, _attrs(std::move(attrs))
{
_opts = _selection->get_query_options();
_opts.set_if<query::partition_slice::option::bypass_cache>(_parameters->bypass_cache());
@@ -161,10 +159,6 @@ select_statement::select_statement(schema_ptr schema,
_opts.set_if<query::partition_slice::option::reversed>(_is_reversed);
}
db::timeout_clock::duration select_statement::get_timeout(const query_options& options) const {
return _attrs->is_timeout_set() ? _attrs->get_timeout(options) : options.get_timeout_config().*get_timeout_config_selector();
}
::shared_ptr<const cql3::metadata> select_statement::get_result_metadata() const {
// FIXME: COUNT needs special result metadata handling.
return _selection->get_result_metadata();
@@ -176,10 +170,9 @@ uint32_t select_statement::get_bound_terms() const {
future<> select_statement::check_access(service::storage_proxy& proxy, const service::client_state& state) const {
try {
const database& db = proxy.local_db();
auto&& s = db.find_schema(keyspace(), column_family());
auto&& s = proxy.get_db().local().find_schema(keyspace(), column_family());
auto& cf_name = s->is_view() ? s->view_info()->base_name() : column_family();
return state.has_column_family_access(db, keyspace(), cf_name, auth::permission::SELECT);
return state.has_column_family_access(keyspace(), cf_name, auth::permission::SELECT);
} catch (const no_such_column_family& e) {
// Will be validated afterwards.
return make_ready_future<>();
@@ -259,9 +252,10 @@ uint64_t select_statement::do_get_limit(const query_options& options, ::shared_p
if (val.is_unset_value()) {
return default_limit;
}
return with_linearized(*val, [&] (bytes_view bv) {
try {
int32_type->validate(*val, options.get_cql_serialization_format());
auto l = value_cast<int32_t>(int32_type->deserialize(*val));
int32_type->validate(bv, options.get_cql_serialization_format());
auto l = value_cast<int32_t>(int32_type->deserialize(bv));
if (l <= 0) {
throw exceptions::invalid_request_exception("LIMIT must be strictly positive");
}
@@ -269,6 +263,7 @@ uint64_t select_statement::do_get_limit(const query_options& options, ::shared_p
} catch (const marshal_exception& e) {
throw exceptions::invalid_request_exception("Invalid limit value");
}
});
}
bool select_statement::needs_post_query_ordering() const {
@@ -371,7 +366,7 @@ select_statement::do_execute(service::storage_proxy& proxy,
}
command->slice.options.set<query::partition_slice::option::allow_short_read>();
auto timeout_duration = get_timeout(options);
auto timeout_duration = state.get_client_state().get_timeout_config().*get_timeout_config_selector();
auto timeout = db::timeout_clock::now() + timeout_duration;
auto p = service::pager::query_pagers::pager(_schema, _selection,
state, options, command, std::move(key_ranges), restrictions_need_filtering ? _restrictions : nullptr);
@@ -379,14 +374,14 @@ select_statement::do_execute(service::storage_proxy& proxy,
if (aggregate || nonpaged_filtering) {
return do_with(
cql3::selection::result_set_builder(*_selection, now,
options.get_cql_serialization_format(), *_group_by_cell_indices), std::move(p),
[this, page_size, now, timeout, restrictions_need_filtering](auto& builder, std::unique_ptr<service::pager::query_pager>& p) {
return do_until([&p] {return p->is_exhausted();},
[&p, &builder, page_size, now, timeout] {
options.get_cql_serialization_format(), *_group_by_cell_indices),
[this, p, page_size, now, timeout, restrictions_need_filtering](auto& builder) {
return do_until([p] {return p->is_exhausted();},
[p, &builder, page_size, now, timeout] {
return p->fetch_page(builder, page_size, now, timeout);
}
).then([this, &p, &builder, restrictions_need_filtering] {
return builder.with_thread_if_needed([this, &p, &builder, restrictions_need_filtering] {
).then([this, p, &builder, restrictions_need_filtering] {
return builder.with_thread_if_needed([this, p, &builder, restrictions_need_filtering] {
auto rs = builder.build();
if (restrictions_need_filtering) {
_stats.filtered_rows_read_total += p->stats().rows_read_total;
@@ -407,7 +402,7 @@ select_statement::do_execute(service::storage_proxy& proxy,
}
if (_selection->is_trivial() && !restrictions_need_filtering && !_per_partition_limit) {
return p->fetch_page_generator(page_size, now, timeout, _stats).then([this, p = std::move(p)] (result_generator generator) {
return p->fetch_page_generator(page_size, now, timeout, _stats).then([this, p] (result_generator generator) {
auto meta = [&] () -> shared_ptr<const cql3::metadata> {
if (!p->is_exhausted()) {
auto meta = make_shared<metadata>(*_selection->get_result_metadata());
@@ -425,7 +420,7 @@ select_statement::do_execute(service::storage_proxy& proxy,
}
return p->fetch_page(page_size, now, timeout).then(
[this, p = std::move(p), &options, now, restrictions_need_filtering](std::unique_ptr<cql3::result_set> rs) {
[this, p, &options, now, restrictions_need_filtering](std::unique_ptr<cql3::result_set> rs) {
if (!p->is_exhausted()) {
rs->get_metadata().set_paging_state(p->state());
@@ -452,7 +447,7 @@ generate_base_key_from_index_pk(const partition_key& index_pk, const std::option
return KeyType::make_empty();
}
std::vector<managed_bytes_view> exploded_base_key;
std::vector<bytes_view> exploded_base_key;
exploded_base_key.reserve(base_columns.size());
for (const column_definition& base_col : base_columns) {
@@ -518,7 +513,7 @@ indexed_table_select_statement::do_execute_base_query(
lw_shared_ptr<const service::pager::paging_state> paging_state) const {
using value_type = std::tuple<foreign_ptr<lw_shared_ptr<query::result>>, lw_shared_ptr<query::read_command>>;
auto cmd = prepare_command_for_base_query(proxy, options, state, now, bool(paging_state));
auto timeout = db::timeout_clock::now() + get_timeout(options);
auto timeout = db::timeout_clock::now() + state.get_client_state().get_timeout_config().*get_timeout_config_selector();
uint32_t queried_ranges_count = partition_ranges.size();
service::query_ranges_to_vnodes_generator ranges_to_vnodes(proxy.get_token_metadata_ptr(), _schema, std::move(partition_ranges));
@@ -612,7 +607,7 @@ indexed_table_select_statement::do_execute_base_query(
lw_shared_ptr<const service::pager::paging_state> paging_state) const {
using value_type = std::tuple<foreign_ptr<lw_shared_ptr<query::result>>, lw_shared_ptr<query::read_command>>;
auto cmd = prepare_command_for_base_query(proxy, options, state, now, bool(paging_state));
auto timeout = db::timeout_clock::now() + get_timeout(options);
auto timeout = db::timeout_clock::now() + state.get_client_state().get_timeout_config().*get_timeout_config_selector();
struct base_query_state {
query::result_merger merger;
@@ -694,7 +689,7 @@ select_statement::execute(service::storage_proxy& proxy,
// is specified we need to get "limit" rows from each partition since there
// is no way to tell which of these rows belong to the query result before
// doing post-query ordering.
auto timeout = db::timeout_clock::now() + get_timeout(options);
auto timeout = db::timeout_clock::now() + state.get_client_state().get_timeout_config().*get_timeout_config_selector();
if (needs_post_query_ordering() && _limit) {
return do_with(std::forward<dht::partition_range_vector>(partition_ranges), [this, &proxy, &state, &options, cmd, timeout](auto& prs) {
assert(cmd->partition_limit == query::max_partitions);
@@ -797,9 +792,8 @@ primary_key_select_statement::primary_key_select_statement(schema_ptr schema, ui
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
cql_stats &stats,
std::unique_ptr<attributes> attrs)
: select_statement{schema, bound_terms, parameters, selection, restrictions, group_by_cell_indices, is_reversed, ordering_comparator, limit, per_partition_limit, stats, std::move(attrs)}
cql_stats &stats)
: select_statement{schema, bound_terms, parameters, selection, restrictions, group_by_cell_indices, is_reversed, ordering_comparator, limit, per_partition_limit, stats}
{
if (_ks_sel == ks_selector::NONSYSTEM) {
if (_restrictions->need_filtering() ||
@@ -825,8 +819,7 @@ indexed_table_select_statement::prepare(database& db,
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
cql_stats &stats,
std::unique_ptr<attributes> attrs)
cql_stats &stats)
{
auto& sim = db.find_column_family(schema).get_index_manager();
auto [index_opt, used_index_restrictions] = restrictions->find_idx(sim);
@@ -852,8 +845,7 @@ indexed_table_select_statement::prepare(database& db,
stats,
*index_opt,
std::move(used_index_restrictions),
view_schema,
std::move(attrs));
view_schema);
}
@@ -869,9 +861,8 @@ indexed_table_select_statement::indexed_table_select_statement(schema_ptr schema
cql_stats &stats,
const secondary_index::index& index,
::shared_ptr<restrictions::restrictions> used_index_restrictions,
schema_ptr view_schema,
std::unique_ptr<attributes> attrs)
: select_statement{schema, bound_terms, parameters, selection, restrictions, group_by_cell_indices, is_reversed, ordering_comparator, limit, per_partition_limit, stats, std::move(attrs)}
schema_ptr view_schema)
: select_statement{schema, bound_terms, parameters, selection, restrictions, group_by_cell_indices, is_reversed, ordering_comparator, limit, per_partition_limit, stats}
, _index{index}
, _used_index_restrictions(used_index_restrictions)
, _view_schema(view_schema)
@@ -887,7 +878,7 @@ indexed_table_select_statement::indexed_table_select_statement(schema_ptr schema
template<typename KeyType>
requires (std::is_same_v<KeyType, partition_key> || std::is_same_v<KeyType, clustering_key_prefix>)
static void append_base_key_to_index_ck(std::vector<managed_bytes_view>& exploded_index_ck, const KeyType& base_key, const column_definition& index_cdef) {
static void append_base_key_to_index_ck(std::vector<bytes_view>& exploded_index_ck, const KeyType& base_key, const column_definition& index_cdef) {
auto key_view = base_key.view();
auto begin = key_view.begin();
if ((std::is_same_v<KeyType, partition_key> && index_cdef.is_partition_key())
@@ -942,7 +933,7 @@ lw_shared_ptr<const service::pager::paging_state> indexed_table_select_statement
}
}();
std::vector<managed_bytes_view> exploded_index_ck;
std::vector<bytes_view> exploded_index_ck;
exploded_index_ck.reserve(_view_schema->clustering_key_size());
bytes token_bytes;
@@ -1244,7 +1235,7 @@ indexed_table_select_statement::read_posting_list(service::storage_proxy& proxy,
auto p = service::pager::query_pagers::pager(_view_schema, selection,
state, options, cmd, std::move(partition_ranges), nullptr);
return p->fetch_page(options.get_page_size(), now, timeout).then([p = std::move(p), &options, limit, now] (std::unique_ptr<cql3::result_set> rs) {
return p->fetch_page(options.get_page_size(), now, timeout).then([p, &options, limit, now] (std::unique_ptr<cql3::result_set> rs) {
rs->get_metadata().set_paging_state(p->state());
return ::make_shared<cql_transport::messages::result_message::rows>(result(std::move(rs)));
});
@@ -1259,7 +1250,7 @@ indexed_table_select_statement::find_index_partition_ranges(service::storage_pro
{
using value_type = std::tuple<dht::partition_range_vector, lw_shared_ptr<const service::pager::paging_state>>;
auto now = gc_clock::now();
auto timeout = db::timeout_clock::now() + get_timeout(options);
auto timeout = db::timeout_clock::now() + state.get_client_state().get_timeout_config().*get_timeout_config_selector();
return read_posting_list(proxy, options, get_limit(options), state, now, timeout, false).then(
[this, now, &options] (::shared_ptr<cql_transport::messages::result_message::rows> rows) {
auto rs = cql3::untyped_result_set(rows);
@@ -1300,7 +1291,7 @@ indexed_table_select_statement::find_index_clustering_rows(service::storage_prox
{
using value_type = std::tuple<std::vector<indexed_table_select_statement::primary_key>, lw_shared_ptr<const service::pager::paging_state>>;
auto now = gc_clock::now();
auto timeout = db::timeout_clock::now() + get_timeout(options);
auto timeout = db::timeout_clock::now() + state.get_client_state().get_timeout_config().*get_timeout_config_selector();
return read_posting_list(proxy, options, get_limit(options), state, now, timeout, true).then(
[this, now, &options] (::shared_ptr<cql_transport::messages::result_message::rows> rows) {
@@ -1327,23 +1318,13 @@ indexed_table_select_statement::find_index_clustering_rows(service::storage_prox
namespace raw {
static void validate_attrs(const cql3::attributes::raw& attrs) {
if (attrs.timestamp) {
throw exceptions::invalid_request_exception("Specifying TIMESTAMP is not legal for SELECT statement");
}
if (attrs.time_to_live) {
throw exceptions::invalid_request_exception("Specifying TTL is not legal for SELECT statement");
}
}
select_statement::select_statement(::shared_ptr<cf_name> cf_name,
lw_shared_ptr<const parameters> parameters,
std::vector<::shared_ptr<selection::raw_selector>> select_clause,
std::vector<::shared_ptr<relation>> where_clause,
::shared_ptr<term::raw> limit,
::shared_ptr<term::raw> per_partition_limit,
std::vector<::shared_ptr<cql3::column_identifier::raw>> group_by_columns,
std::unique_ptr<attributes::raw> attrs)
std::vector<::shared_ptr<cql3::column_identifier::raw>> group_by_columns)
: cf_statement(std::move(cf_name))
, _parameters(std::move(parameters))
, _select_clause(std::move(select_clause))
@@ -1351,10 +1332,7 @@ select_statement::select_statement(::shared_ptr<cf_name> cf_name,
, _limit(std::move(limit))
, _per_partition_limit(std::move(per_partition_limit))
, _group_by_columns(std::move(group_by_columns))
, _attrs(std::move(attrs))
{
validate_attrs(*_attrs);
}
{ }
void select_statement::maybe_jsonize_select_clause(database& db, schema_ptr schema) {
// Fill wildcard clause with explicit column identifiers for as_json function
@@ -1425,8 +1403,6 @@ std::unique_ptr<prepared_statement> select_statement::prepare(database& db, cql_
auto group_by_cell_indices = ::make_shared<std::vector<size_t>>(prepare_group_by(*schema, *selection));
::shared_ptr<cql3::statements::select_statement> stmt;
auto prepared_attrs = _attrs->prepare(db, keyspace(), column_family());
prepared_attrs->collect_marker_specification(bound_names);
if (restrictions->uses_secondary_indexing()) {
stmt = indexed_table_select_statement::prepare(
db,
@@ -1440,8 +1416,7 @@ std::unique_ptr<prepared_statement> select_statement::prepare(database& db, cql_
std::move(ordering_comparator),
prepare_limit(db, bound_names, _limit),
prepare_limit(db, bound_names, _per_partition_limit),
stats,
std::move(prepared_attrs));
stats);
} else {
stmt = ::make_shared<cql3::statements::primary_key_select_statement>(
schema,
@@ -1454,8 +1429,7 @@ std::unique_ptr<prepared_statement> select_statement::prepare(database& db, cql_
std::move(ordering_comparator),
prepare_limit(db, bound_names, _limit),
prepare_limit(db, bound_names, _per_partition_limit),
stats,
std::move(prepared_attrs));
stats);
}
auto partition_key_bind_indices = bound_names.get_partition_key_bind_indexes(*schema);
@@ -1473,7 +1447,7 @@ select_statement::prepare_restrictions(database& db,
{
try {
return ::make_shared<restrictions::statement_restrictions>(db, schema, statement_type::SELECT, std::move(_where_clause), bound_names,
selection->contains_only_static_columns(), for_view, allow_filtering);
selection->contains_only_static_columns(), selection->contains_a_collection(), for_view, allow_filtering);
} catch (const exceptions::unrecognized_entity_exception& e) {
if (contains_alias(e.entity)) {
throw exceptions::invalid_request_exception(format("Aliases aren't allowed in the where clause ('{}')", e.relation_str));

View File

@@ -96,7 +96,6 @@ protected:
const ks_selector _ks_sel;
bool _range_scan = false;
bool _range_scan_no_bypass_cache = false;
std::unique_ptr<cql3::attributes> _attrs;
protected :
virtual future<::shared_ptr<cql_transport::messages::result_message>> do_execute(service::storage_proxy& proxy,
service::query_state& state, const query_options& options) const;
@@ -112,8 +111,7 @@ public:
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
cql_stats& stats,
std::unique_ptr<cql3::attributes> attrs);
cql_stats& stats);
virtual ::shared_ptr<const cql3::metadata> get_result_metadata() const override;
virtual uint32_t get_bound_terms() const override;
@@ -147,8 +145,6 @@ public:
bool has_group_by() const { return _group_by_cell_indices && !_group_by_cell_indices->empty(); }
db::timeout_clock::duration get_timeout(const query_options& options) const;
protected:
uint64_t do_get_limit(const query_options& options, ::shared_ptr<term> limit, uint64_t default_limit) const;
uint64_t get_limit(const query_options& options) const {
@@ -175,8 +171,7 @@ public:
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
cql_stats &stats,
std::unique_ptr<cql3::attributes> attrs);
cql_stats &stats);
};
class indexed_table_select_statement : public select_statement {
@@ -197,8 +192,7 @@ public:
ordering_comparator_type ordering_comparator,
::shared_ptr<term> limit,
::shared_ptr<term> per_partition_limit,
cql_stats &stats,
std::unique_ptr<cql3::attributes> attrs);
cql_stats &stats);
indexed_table_select_statement(schema_ptr schema,
uint32_t bound_terms,
@@ -213,8 +207,7 @@ public:
cql_stats &stats,
const secondary_index::index& index,
::shared_ptr<restrictions::restrictions> used_index_restrictions,
schema_ptr view_schema,
std::unique_ptr<cql3::attributes> attrs);
schema_ptr view_schema);
private:
virtual future<::shared_ptr<cql_transport::messages::result_message>> do_execute(service::storage_proxy& proxy,

Some files were not shown because too many files have changed in this diff Show More