Compare commits
99 Commits
branch-3.1
...
next-2.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d27eb734a7 | ||
|
|
e6aeb490b5 | ||
|
|
2e3b09b593 | ||
|
|
92c74f4e0b | ||
|
|
89d835e9e3 | ||
|
|
263a740084 | ||
|
|
7f24b5319e | ||
|
|
fe16c0e985 | ||
|
|
f85badaaac | ||
|
|
2193d41683 | ||
|
|
1e1f0c29bf | ||
|
|
84d4588b5f | ||
|
|
7b43b26709 | ||
|
|
0ed01acf15 | ||
|
|
7ce160f408 | ||
|
|
5017d9b46a | ||
|
|
50b6ab3552 | ||
|
|
b1652823aa | ||
|
|
02b24aec34 | ||
|
|
22eea4d8cf | ||
|
|
d257f6d57c | ||
|
|
6fca92ac3c | ||
|
|
26e3917046 | ||
|
|
3892594a93 | ||
|
|
4b24439841 | ||
|
|
a02a4592d8 | ||
|
|
b6e1c08451 | ||
|
|
9469afcd27 | ||
|
|
240b9f122b | ||
|
|
cb16cd7724 | ||
|
|
c864d198fc | ||
|
|
25125e9c4f | ||
|
|
faf10fe6aa | ||
|
|
f76269cdcf | ||
|
|
a9b0ccf116 | ||
|
|
abc5941f87 | ||
|
|
a152ac12af | ||
|
|
c274fdf2ec | ||
|
|
5b88d6b4d6 | ||
|
|
2d626e1cf8 | ||
|
|
c11bd3e1cf | ||
|
|
9df3df92bc | ||
|
|
8ad9578a6c | ||
|
|
4cb6061a9f | ||
|
|
1940e6bd95 | ||
|
|
044cfde5f3 | ||
|
|
262a246436 | ||
|
|
799dbb4f2e | ||
|
|
a2fe669dd3 | ||
|
|
56de761daf | ||
|
|
c3187093a3 | ||
|
|
111c2ecf5d | ||
|
|
a6ecdbbba6 | ||
|
|
17cc62d0b3 | ||
|
|
eb646c61ed | ||
|
|
782d817e84 | ||
|
|
3ed5e63e8a | ||
|
|
d17ce46983 | ||
|
|
7ca5e7e993 | ||
|
|
07b0ce27fa | ||
|
|
27be3cd242 | ||
|
|
abf50aafef | ||
|
|
dfe5b38a43 | ||
|
|
9bdc8c25f5 | ||
|
|
e75c55b2db | ||
|
|
756feae052 | ||
|
|
202b4e6797 | ||
|
|
76ac200eff | ||
|
|
9aa172fe8e | ||
|
|
c4af043ef7 | ||
|
|
06b25320be | ||
|
|
ff70d9f15c | ||
|
|
9bbd5821a2 | ||
|
|
a7841f1f2e | ||
|
|
84859e0745 | ||
|
|
6b74e1f02d | ||
|
|
520f17b315 | ||
|
|
9fe3d04f31 | ||
|
|
a74183eb1e | ||
|
|
e059f17bf2 | ||
|
|
0e8e005357 | ||
|
|
8bf6f39392 | ||
|
|
04ba51986e | ||
|
|
1d5379c462 | ||
|
|
cb5dc56bfd | ||
|
|
b578b492cd | ||
|
|
30c950a7f6 | ||
|
|
f0d1e9c518 | ||
|
|
597aeca93d | ||
|
|
1a94b90a4d | ||
|
|
acdd42c7c8 | ||
|
|
bd4f658555 | ||
|
|
a983ba7aad | ||
|
|
0a561fc326 | ||
|
|
1f10549056 | ||
|
|
c2a2560ea3 | ||
|
|
237e36a0b4 | ||
|
|
e78c137bfc | ||
|
|
fb99a7c902 |
@@ -1,3 +0,0 @@
|
||||
.git
|
||||
build
|
||||
seastar/build
|
||||
6
.github/ISSUE_TEMPLATE.md
vendored
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,9 +1,3 @@
|
||||
This is Scylla's bug tracker, to be used for reporting bugs only.
|
||||
If you have a question about Scylla, and not a bug, please ask it in
|
||||
our mailing-list at scylladb-dev@googlegroups.com or in our slack channel.
|
||||
|
||||
- [] I have read the disclaimer above, and I am reporting a suspected malfunction in Scylla.
|
||||
|
||||
*Installation details*
|
||||
Scylla version (or git commit hash):
|
||||
Cluster size:
|
||||
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,4 +0,0 @@
|
||||
Scylla doesn't use pull-requests, please send a patch to the [mailing list](mailto:scylladb-dev@googlegroups.com) instead.
|
||||
See our [contributing guidelines](../CONTRIBUTING.md) and our [Scylla development guidelines](../HACKING.md) for more information.
|
||||
|
||||
If you have any questions please don't hesitate to send a mail to the [dev list](mailto:scylladb-dev@googlegroups.com).
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,4 +18,3 @@ CMakeLists.txt.user
|
||||
*.egg-info
|
||||
__pycache__CMakeLists.txt.user
|
||||
.gdbinit
|
||||
resources
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -6,9 +6,9 @@
|
||||
path = swagger-ui
|
||||
url = ../scylla-swagger-ui
|
||||
ignore = dirty
|
||||
[submodule "dist/ami/files/scylla-ami"]
|
||||
path = dist/ami/files/scylla-ami
|
||||
url = ../scylla-ami
|
||||
[submodule "xxHash"]
|
||||
path = xxHash
|
||||
url = ../xxHash
|
||||
[submodule "libdeflate"]
|
||||
path = libdeflate
|
||||
url = ../libdeflate
|
||||
|
||||
@@ -138,5 +138,4 @@ target_include_directories(scylla PUBLIC
|
||||
${SEASTAR_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
xxhash
|
||||
libdeflate
|
||||
build/release/gen)
|
||||
|
||||
85
HACKING.md
85
HACKING.md
@@ -20,13 +20,11 @@ $ git submodule update --init --recursive
|
||||
|
||||
Scylla depends on the system package manager for its development dependencies.
|
||||
|
||||
Running `./install-dependencies.sh` (as root) installs the appropriate packages based on your Linux distribution.
|
||||
Running `./install_dependencies.sh` (as root) installs the appropriate packages based on your Linux distribution.
|
||||
|
||||
### Build system
|
||||
|
||||
**Note**: Compiling Scylla requires, conservatively, 2 GB of memory per native
|
||||
thread, and up to 3 GB per native thread while linking. GCC >= 8.1.1. is
|
||||
required.
|
||||
**Note**: Compiling Scylla requires, conservatively, 2 GB of memory per native thread, and up to 3 GB per native thread while linking.
|
||||
|
||||
Scylla is built with [Ninja](https://ninja-build.org/), a low-level rule-based system. A Python script, `configure.py`, generates a Ninja file (`build.ninja`) based on configuration options.
|
||||
|
||||
@@ -45,7 +43,9 @@ The full suite of options for project configuration is available via
|
||||
$ ./configure.py --help
|
||||
```
|
||||
|
||||
The most important option is:
|
||||
The most important options are:
|
||||
|
||||
- `--mode={release,debug,all}`: Debug mode enables [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) and allows for debugging with tools like GDB. Debugging builds are generally slower and generate much larger object files than release builds.
|
||||
|
||||
- `--{enable,disable}-dpdk`: [DPDK](http://dpdk.org/) is a set of libraries and drivers for fast packet processing. During development, it's not necessary to enable support even if it is supported by your platform.
|
||||
|
||||
@@ -57,29 +57,6 @@ To save time -- for instance, to avoid compiling all unit tests -- you can also
|
||||
$ ninja-build build/release/tests/schema_change_test
|
||||
```
|
||||
|
||||
You can also specify a single mode. For example
|
||||
|
||||
```bash
|
||||
$ ninja-build release
|
||||
```
|
||||
|
||||
Will build everytihng in release mode. The valid modes are
|
||||
|
||||
* Debug: Enables [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer)
|
||||
and other sanity checks. It has no optimizations, which allows for debugging with tools like
|
||||
GDB. Debugging builds are generally slower and generate much larger object files than release builds.
|
||||
* Release: Fewer checks and more optimizations. It still has debug info.
|
||||
* Dev: No optimizations or debug info. The objective is to compile and link as fast as possible.
|
||||
This is useful for the first iterations of a patch.
|
||||
|
||||
|
||||
Note that by default unit tests binaries are stripped so they can't be used with gdb or seastar-addr2line.
|
||||
To include debug information in the unit test binary, build the test binary with a `_g` suffix. For example,
|
||||
|
||||
```bash
|
||||
$ ninja-build build/release/tests/schema_change_test_g
|
||||
```
|
||||
|
||||
### Unit testing
|
||||
|
||||
Unit tests live in the `/tests` directory. Like with application source files, test sources and executables are specified manually in `configure.py` and need to be updated when changes are made.
|
||||
@@ -106,7 +83,7 @@ The `-c1 -m1G` arguments limit this Seastar-based test to a single system thread
|
||||
|
||||
### Preparing patches
|
||||
|
||||
All changes to Scylla are submitted as patches to the public [mailing list](mailto:scylladb-dev@googlegroups.com). Once a patch is approved by one of the maintainers of the project, it is committed to the maintainers' copy of the repository at https://github.com/scylladb/scylla.
|
||||
All changes to Scylla are submitted as patches to the public mailing list. Once a patch is approved by one of the maintainers of the project, it is committed to the maintainers' copy of the repository at https://github.com/scylladb/scylla.
|
||||
|
||||
Detailed instructions for formatting patches for the mailing list and advice on preparing good patches are available at the [ScyllaDB website](http://docs.scylladb.com/contribute/). There are also some guidelines that can help you make the patch review process smoother:
|
||||
|
||||
@@ -135,8 +112,6 @@ The usual is "Tests: unit (release)", although running debug tests is encouraged
|
||||
|
||||
5. When answering review comments, prefer inline quotes as they make it easier to track the conversation across multiple e-mails.
|
||||
|
||||
6. The Linux kernel's [Submitting Patches](https://www.kernel.org/doc/html/v4.19/process/submitting-patches.html) document offers excellent advice on how to prepare patches and patchsets for review. Since the Scylla development process is derived from the kernel's, almost all of the advice there is directly applicable.
|
||||
|
||||
### Finding a person to review and merge your patches
|
||||
|
||||
You can use the `scripts/find-maintainer` script to find a subsystem maintainer and/or reviewer for your patches. The script accepts a filename in the git source tree as an argument and outputs a list of subsystems the file belongs to and their respective maintainers and reviewers. For example, if you changed the `cql3/statements/create_view_statement.hh` file, run the script as follows:
|
||||
@@ -189,29 +164,6 @@ On a development machine, one might run Scylla as
|
||||
$ SCYLLA_HOME=$HOME/scylla build/release/scylla --overprovisioned --developer-mode=yes
|
||||
```
|
||||
|
||||
To interact with scylla it is recommended to build our versions of
|
||||
cqlsh and nodetool. They are available at
|
||||
https://github.com/scylladb/scylla-tools-java and can be built with
|
||||
|
||||
```bash
|
||||
$ ./install-dependencies.sh
|
||||
$ ant jar
|
||||
```
|
||||
|
||||
cqlsh should work out of the box, but nodetool depends on a running
|
||||
scylla-jmx (https://github.com/scylladb/scylla-jmx). It can be build
|
||||
with
|
||||
|
||||
```bash
|
||||
$ mvn package
|
||||
```
|
||||
|
||||
and must be started with
|
||||
|
||||
```bash
|
||||
$ ./scripts/scylla-jmx
|
||||
```
|
||||
|
||||
### Branches and tags
|
||||
|
||||
Multiple release branches are maintained on the Git repository at https://github.com/scylladb/scylla. Release 1.5, for instance, is tracked on the `branch-1.5` branch.
|
||||
@@ -302,7 +254,7 @@ In this example, `10.0.0.2` will be sent up to 16 jobs and the local machine wil
|
||||
|
||||
When a compilation is in progress, the status of jobs on all remote machines can be visualized in the terminal with `distccmon-text` or graphically as a GTK application with `distccmon-gnome`.
|
||||
|
||||
One thing to keep in mind is that linking object files happens on the coordinating machine, which can be a bottleneck. See the next sections speeding up this process.
|
||||
One thing to keep in mind is that linking object files happens on the coordinating machine, which can be a bottleneck. See the next section speeding up this process.
|
||||
|
||||
### Using the `gold` linker
|
||||
|
||||
@@ -312,24 +264,6 @@ Linking Scylla can be slow. The gold linker can replace GNU ld and often speeds
|
||||
$ sudo alternatives --config ld
|
||||
```
|
||||
|
||||
### Using split dwarf
|
||||
|
||||
With debug info enabled, most of the link time is spent copying and
|
||||
relocating it. It is possible to leave most of the debug info out of
|
||||
the link by writing it to a side .dwo file. This is done by passing
|
||||
`-gsplit-dwarf` to gcc.
|
||||
|
||||
Unfortunately just `-gsplit-dwarf` would slow down `gdb` startup. To
|
||||
avoid that the gold linker can be told to create an index with
|
||||
`--gdb-index`.
|
||||
|
||||
More info at https://gcc.gnu.org/wiki/DebugFission.
|
||||
|
||||
Both options can be enable by passing `--split-dwarf` to configure.py.
|
||||
|
||||
Note that distcc is *not* compatible with it, but icecream
|
||||
(https://github.com/icecc/icecream) is.
|
||||
|
||||
### Testing changes in Seastar with Scylla
|
||||
|
||||
Sometimes Scylla development is closely tied with a feature being developed in Seastar. It can be useful to compile Scylla with a particular check-out of Seastar.
|
||||
@@ -343,8 +277,3 @@ $ git remote add local /home/tsmith/src/seastar
|
||||
$ git remote update
|
||||
$ git checkout -t local/my_local_seastar_branch
|
||||
```
|
||||
|
||||
### Core dump debugging
|
||||
|
||||
Slides:
|
||||
2018.11.20: https://www.slideshare.net/tomekgrabiec/scylla-core-dump-debugging-tools
|
||||
|
||||
@@ -13,11 +13,6 @@ $ # Rejoice!
|
||||
|
||||
Please see [HACKING.md](HACKING.md) for detailed information on building and developing Scylla.
|
||||
|
||||
**Note**: GCC >= 8.1.1 is require to compile Scylla.
|
||||
|
||||
**Note**: See [frozen toolchain](tools/toolchain/README.md) for a way to build and run
|
||||
on an older distribution.
|
||||
|
||||
## Running Scylla
|
||||
|
||||
* Run Scylla
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
PRODUCT=scylla
|
||||
VERSION=3.1.4
|
||||
VERSION=2.2.2
|
||||
|
||||
if test -f version
|
||||
then
|
||||
@@ -23,4 +22,3 @@ echo "$SCYLLA_VERSION-$SCYLLA_RELEASE"
|
||||
mkdir -p build
|
||||
echo "$SCYLLA_VERSION" > build/SCYLLA-VERSION-FILE
|
||||
echo "$SCYLLA_RELEASE" > build/SCYLLA-RELEASE-FILE
|
||||
echo "$PRODUCT" > build/SCYLLA-PRODUCT-FILE
|
||||
|
||||
@@ -455,7 +455,7 @@
|
||||
"operations":[
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Returns a list of sstable filenames that contain the given partition key on this node",
|
||||
"summary":"Returns a list of filenames that contain the given key on this node",
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"string"
|
||||
@@ -475,7 +475,7 @@
|
||||
},
|
||||
{
|
||||
"name":"key",
|
||||
"description":"The partition key. In a composite-key scenario, use ':' to separate the columns in the key.",
|
||||
"description":"The key",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
@@ -611,54 +611,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/column_family/toppartitions/{name}",
|
||||
"operations":[
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Toppartitions query",
|
||||
"type":"toppartitions_query_results",
|
||||
"nickname":"toppartitions",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"name",
|
||||
"description":"The column family name in keyspace:name format",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"path"
|
||||
},
|
||||
{
|
||||
"name":"duration",
|
||||
"description":"Duration (in milliseconds) of monitoring operation",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"int",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"list_size",
|
||||
"description":"number of the top partitions to list",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"int",
|
||||
"paramType":"query"
|
||||
},
|
||||
{
|
||||
"name":"capacity",
|
||||
"description":"capacity of stream summary: determines amount of resources used in query processing",
|
||||
"required":false,
|
||||
"allowMultiple":false,
|
||||
"type":"int",
|
||||
"paramType":"query"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/column_family/metrics/memtable_columns_count/",
|
||||
"operations":[
|
||||
@@ -2864,44 +2816,6 @@
|
||||
"description":"The column family type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"toppartitions_record":{
|
||||
"id":"toppartitions_record",
|
||||
"description":"nodetool toppartitions query record",
|
||||
"properties":{
|
||||
"partition":{
|
||||
"type":"string",
|
||||
"description":"Partition key"
|
||||
},
|
||||
"count":{
|
||||
"type":"long",
|
||||
"description":"Number of read/write operations"
|
||||
},
|
||||
"error":{
|
||||
"type":"long",
|
||||
"description":"Indication of inaccuracy in counting PKs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"toppartitions_query_results":{
|
||||
"id":"toppartitions_query_results",
|
||||
"description":"nodetool toppartitions query results",
|
||||
"properties":{
|
||||
"read":{
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"toppartitions_record"
|
||||
},
|
||||
"description":"Read results"
|
||||
},
|
||||
"write":{
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"toppartitions_record"
|
||||
},
|
||||
"description":"Write results"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
"/v2/config/{id}": {
|
||||
"get": {
|
||||
"description": "Return a config value",
|
||||
"operationId": "find_config_id",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": ["config"],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "ID of config to return",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Config value"
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorModel"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2129,41 +2129,6 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/storage_service/view_build_statuses/{keyspace}/{view}",
|
||||
"operations":[
|
||||
{
|
||||
"method":"GET",
|
||||
"summary":"Gets the progress of a materialized view build",
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"mapper"
|
||||
},
|
||||
"nickname":"view_build_statuses",
|
||||
"produces":[
|
||||
"application/json"
|
||||
],
|
||||
"parameters":[
|
||||
{
|
||||
"name":"keyspace",
|
||||
"description":"The keyspace",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"path"
|
||||
},
|
||||
{
|
||||
"name":"view",
|
||||
"description":"View name",
|
||||
"required":true,
|
||||
"allowMultiple":false,
|
||||
"type":"string",
|
||||
"paramType":"path"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models":{
|
||||
|
||||
12
api/api.cc
12
api/api.cc
@@ -20,9 +20,9 @@
|
||||
*/
|
||||
|
||||
#include "api.hh"
|
||||
#include <seastar/http/file_handler.hh>
|
||||
#include <seastar/http/transformers.hh>
|
||||
#include <seastar/http/api_docs.hh>
|
||||
#include "http/file_handler.hh"
|
||||
#include "http/transformers.hh"
|
||||
#include "http/api_docs.hh"
|
||||
#include "storage_service.hh"
|
||||
#include "commitlog.hh"
|
||||
#include "gossiper.hh"
|
||||
@@ -36,12 +36,9 @@
|
||||
#include "endpoint_snitch.hh"
|
||||
#include "compaction_manager.hh"
|
||||
#include "hinted_handoff.hh"
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "http/exception.hh"
|
||||
#include "stream_manager.hh"
|
||||
#include "system.hh"
|
||||
#include "api/config.hh"
|
||||
|
||||
logging::logger apilog("api");
|
||||
|
||||
namespace api {
|
||||
|
||||
@@ -68,7 +65,6 @@ future<> set_server_init(http_context& ctx) {
|
||||
rb->set_api_doc(r);
|
||||
rb02->set_api_doc(r);
|
||||
rb02->register_api_file(r, "swagger20_header");
|
||||
set_config(rb02, ctx, r);
|
||||
rb->register_function(r, "system",
|
||||
"The system related API");
|
||||
set_system(ctx, r);
|
||||
|
||||
44
api/api.hh
44
api/api.hh
@@ -21,15 +21,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <seastar/json/json_elements.hh>
|
||||
#include <type_traits>
|
||||
#include "json/json_elements.hh"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/units/detail/utility.hpp>
|
||||
#include "api/api-doc/utils.json.hh"
|
||||
#include "utils/histogram.hh"
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "http/exception.hh"
|
||||
#include "api_init.hh"
|
||||
#include "seastarx.hh"
|
||||
|
||||
@@ -218,42 +216,4 @@ std::vector<T> concat(std::vector<T> a, std::vector<T>&& b) {
|
||||
return a;
|
||||
}
|
||||
|
||||
template <class T, class Base = T>
|
||||
class req_param {
|
||||
public:
|
||||
sstring name;
|
||||
sstring param;
|
||||
T value;
|
||||
|
||||
req_param(const request& req, sstring name, T default_val) : name(name) {
|
||||
param = req.get_query_param(name);
|
||||
if (param.empty()) {
|
||||
value = default_val;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// boost::lexical_cast does not use boolalpha. Converting a
|
||||
// true/false throws exceptions. We don't want that.
|
||||
if constexpr (std::is_same_v<Base, bool>) {
|
||||
// Cannot use boolalpha because we (probably) want to
|
||||
// accept 1 and 0 as well as true and false. And True. And fAlse.
|
||||
std::transform(param.begin(), param.end(), param.begin(), ::tolower);
|
||||
if (param == "true" || param == "1") {
|
||||
value = T(true);
|
||||
} else if (param == "false" || param == "0") {
|
||||
value = T(false);
|
||||
} else {
|
||||
throw boost::bad_lexical_cast{};
|
||||
}
|
||||
} else {
|
||||
value = T{boost::lexical_cast<Base>(param)};
|
||||
}
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
throw bad_param_exception(format("{} ({}): type error - should be {}", name, param, boost::units::detail::demangle(typeid(Base).name())));
|
||||
}
|
||||
}
|
||||
|
||||
operator T() const { return value; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "database_fwd.hh"
|
||||
#include "database.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
#include <seastar/http/httpd.hh>
|
||||
#include "http/httpd.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
#include "collectd.hh"
|
||||
#include "api/api-doc/collectd.json.hh"
|
||||
#include <seastar/core/scollectd.hh>
|
||||
#include <seastar/core/scollectd_api.hh>
|
||||
#include "core/scollectd.hh"
|
||||
#include "core/scollectd_api.hh"
|
||||
#include "endian.h"
|
||||
#include <boost/range/irange.hpp>
|
||||
#include <regex>
|
||||
|
||||
@@ -22,15 +22,11 @@
|
||||
#include "column_family.hh"
|
||||
#include "api/api-doc/column_family.json.hh"
|
||||
#include <vector>
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "http/exception.hh"
|
||||
#include "sstables/sstables.hh"
|
||||
#include "utils/estimated_histogram.hh"
|
||||
#include <algorithm>
|
||||
|
||||
#include "db/data_listeners.hh"
|
||||
|
||||
extern logging::logger apilog;
|
||||
|
||||
namespace api {
|
||||
using namespace httpd;
|
||||
|
||||
@@ -38,7 +34,7 @@ using namespace std;
|
||||
using namespace json;
|
||||
namespace cf = httpd::column_family_json;
|
||||
|
||||
std::tuple<sstring, sstring> parse_fully_qualified_cf_name(sstring name) {
|
||||
const utils::UUID& get_uuid(const sstring& name, const database& db) {
|
||||
auto pos = name.find("%3A");
|
||||
size_t end;
|
||||
if (pos == sstring::npos) {
|
||||
@@ -50,15 +46,11 @@ std::tuple<sstring, sstring> parse_fully_qualified_cf_name(sstring name) {
|
||||
} else {
|
||||
end = pos + 3;
|
||||
}
|
||||
return std::make_tuple(name.substr(0, pos), name.substr(end));
|
||||
}
|
||||
|
||||
const utils::UUID& get_uuid(const sstring& name, const database& db) {
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(name);
|
||||
try {
|
||||
return db.find_uuid(ks, cf);
|
||||
return db.find_uuid(name.substr(0, pos), name.substr(end));
|
||||
} catch (std::out_of_range& e) {
|
||||
throw bad_param_exception(format("Column family '{}:{}' not found", ks, cf));
|
||||
throw bad_param_exception("Column family '" + name.substr(0, pos) + ":"
|
||||
+ name.substr(end) + "' not found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,27 +166,27 @@ static future<json::json_return_type> get_cf_unleveled_sstables(http_context& ct
|
||||
}, std::plus<int64_t>());
|
||||
}
|
||||
|
||||
static int64_t min_partition_size(column_family& cf) {
|
||||
static int64_t min_row_size(column_family& cf) {
|
||||
int64_t res = INT64_MAX;
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res = std::min(res, i->get_stats_metadata().estimated_partition_size.min());
|
||||
res = std::min(res, i->get_stats_metadata().estimated_row_size.min());
|
||||
}
|
||||
return (res == INT64_MAX) ? 0 : res;
|
||||
}
|
||||
|
||||
static int64_t max_partition_size(column_family& cf) {
|
||||
static int64_t max_row_size(column_family& cf) {
|
||||
int64_t res = 0;
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res = std::max(i->get_stats_metadata().estimated_partition_size.max(), res);
|
||||
res = std::max(i->get_stats_metadata().estimated_row_size.max(), res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static integral_ratio_holder mean_partition_size(column_family& cf) {
|
||||
static integral_ratio_holder mean_row_size(column_family& cf) {
|
||||
integral_ratio_holder res;
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
auto c = i->get_stats_metadata().estimated_partition_size.count();
|
||||
res.sub += i->get_stats_metadata().estimated_partition_size.mean() * c;
|
||||
auto c = i->get_stats_metadata().estimated_row_size.count();
|
||||
res.sub += i->get_stats_metadata().estimated_row_size.mean() * c;
|
||||
res.total += c;
|
||||
}
|
||||
return res;
|
||||
@@ -411,24 +403,22 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
return get_cf_stats(ctx, &column_family::stats::memtable_switch_count);
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_estimated_row_size_histogram.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], utils::estimated_histogram(0), [](column_family& cf) {
|
||||
utils::estimated_histogram res(0);
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res.merge(i->get_stats_metadata().estimated_partition_size);
|
||||
res.merge(i->get_stats_metadata().estimated_row_size);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
utils::estimated_histogram_merge, utils_json::estimated_histogram());
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_estimated_row_count.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), [](column_family& cf) {
|
||||
uint64_t res = 0;
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res += i->get_stats_metadata().estimated_partition_size.count();
|
||||
res += i->get_stats_metadata().estimated_row_size.count();
|
||||
}
|
||||
return res;
|
||||
},
|
||||
@@ -439,7 +429,7 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
return map_reduce_cf(ctx, req->param["name"], utils::estimated_histogram(0), [](column_family& cf) {
|
||||
utils::estimated_histogram res(0);
|
||||
for (auto i: *cf.get_sstables() ) {
|
||||
res.merge(i->get_stats_metadata().estimated_cells_count);
|
||||
res.merge(i->get_stats_metadata().estimated_column_count);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
@@ -556,36 +546,30 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
return sum_sstable(ctx, true);
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_min_row_size.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], INT64_MAX, min_partition_size, min_int64);
|
||||
return map_reduce_cf(ctx, req->param["name"], INT64_MAX, min_row_size, min_int64);
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_all_min_row_size.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, INT64_MAX, min_partition_size, min_int64);
|
||||
return map_reduce_cf(ctx, INT64_MAX, min_row_size, min_int64);
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_max_row_size.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), max_partition_size, max_int64);
|
||||
return map_reduce_cf(ctx, req->param["name"], int64_t(0), max_row_size, max_int64);
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_all_max_row_size.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
return map_reduce_cf(ctx, int64_t(0), max_partition_size, max_int64);
|
||||
return map_reduce_cf(ctx, int64_t(0), max_row_size, max_int64);
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_mean_row_size.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
// Cassandra 3.x mean values are truncated as integrals.
|
||||
return map_reduce_cf(ctx, req->param["name"], integral_ratio_holder(), mean_partition_size, std::plus<integral_ratio_holder>());
|
||||
return map_reduce_cf(ctx, req->param["name"], integral_ratio_holder(), mean_row_size, std::plus<integral_ratio_holder>());
|
||||
});
|
||||
|
||||
// FIXME: this refers to partitions, not rows.
|
||||
cf::get_all_mean_row_size.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
// Cassandra 3.x mean values are truncated as integrals.
|
||||
return map_reduce_cf(ctx, integral_ratio_holder(), mean_partition_size, std::plus<integral_ratio_holder>());
|
||||
return map_reduce_cf(ctx, integral_ratio_holder(), mean_row_size, std::plus<integral_ratio_holder>());
|
||||
});
|
||||
|
||||
cf::get_bloom_filter_false_positives.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
@@ -921,60 +905,5 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
cf::get_sstables_for_key.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
auto key = req->get_query_param("key");
|
||||
auto uuid = get_uuid(req->param["name"], ctx.db.local());
|
||||
|
||||
return ctx.db.map_reduce0([key, uuid] (database& db) {
|
||||
return db.find_column_family(uuid).get_sstables_by_partition_key(key);
|
||||
}, std::unordered_set<sstring>(),
|
||||
[](std::unordered_set<sstring> a, std::unordered_set<sstring>&& b) mutable {
|
||||
a.insert(b.begin(),b.end());
|
||||
return a;
|
||||
}).then([](const std::unordered_set<sstring>& res) {
|
||||
return make_ready_future<json::json_return_type>(container_to_vec(res));
|
||||
});
|
||||
});
|
||||
|
||||
cf::toppartitions.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
auto name_param = req->param["name"];
|
||||
auto [ks, cf] = parse_fully_qualified_cf_name(name_param);
|
||||
|
||||
api::req_param<std::chrono::milliseconds, unsigned> duration{*req, "duration", 1000ms};
|
||||
api::req_param<unsigned> capacity(*req, "capacity", 256);
|
||||
api::req_param<unsigned> list_size(*req, "list_size", 10);
|
||||
|
||||
apilog.info("toppartitions query: name={} duration={} list_size={} capacity={}",
|
||||
name_param, duration.param, list_size.param, capacity.param);
|
||||
|
||||
return seastar::do_with(db::toppartitions_query(ctx.db, ks, cf, duration.value, list_size, capacity), [&ctx](auto& q) {
|
||||
return q.scatter().then([&q] {
|
||||
return sleep(q.duration()).then([&q] {
|
||||
return q.gather(q.capacity()).then([&q] (auto topk_results) {
|
||||
apilog.debug("toppartitions query: processing results");
|
||||
cf::toppartitions_query_results results;
|
||||
|
||||
for (auto& d: topk_results.read.top(q.list_size())) {
|
||||
cf::toppartitions_record r;
|
||||
r.partition = sstring(d.item);
|
||||
r.count = d.count;
|
||||
r.error = d.error;
|
||||
results.read.push(r);
|
||||
}
|
||||
for (auto& d: topk_results.write.top(q.list_size())) {
|
||||
cf::toppartitions_record r;
|
||||
r.partition = sstring(d.item);
|
||||
r.count = d.count;
|
||||
r.error = d.error;
|
||||
results.write.push(r);
|
||||
}
|
||||
return make_ready_future<json::json_return_type>(results);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#include "api.hh"
|
||||
#include "api/api-doc/column_family.json.hh"
|
||||
#include "database.hh"
|
||||
#include <seastar/core/future-util.hh>
|
||||
#include <any>
|
||||
|
||||
namespace api {
|
||||
|
||||
@@ -39,15 +37,9 @@ template<class Mapper, class I, class Reducer>
|
||||
future<I> map_reduce_cf_raw(http_context& ctx, const sstring& name, I init,
|
||||
Mapper mapper, Reducer reducer) {
|
||||
auto uuid = get_uuid(name, ctx.db.local());
|
||||
using mapper_type = std::function<std::any (database&)>;
|
||||
using reducer_type = std::function<std::any (std::any, std::any)>;
|
||||
return ctx.db.map_reduce0(mapper_type([mapper, uuid](database& db) {
|
||||
return I(mapper(db.find_column_family(uuid)));
|
||||
}), std::any(std::move(init)), reducer_type([reducer = std::move(reducer)] (std::any a, std::any b) mutable {
|
||||
return I(reducer(std::any_cast<I>(std::move(a)), std::any_cast<I>(std::move(b))));
|
||||
})).then([] (std::any r) {
|
||||
return std::any_cast<I>(std::move(r));
|
||||
});
|
||||
return ctx.db.map_reduce0([mapper, uuid](database& db) {
|
||||
return mapper(db.find_column_family(uuid));
|
||||
}, init, reducer);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,43 +51,35 @@ future<json::json_return_type> map_reduce_cf(http_context& ctx, const sstring& n
|
||||
});
|
||||
}
|
||||
|
||||
template<class Mapper, class I, class Reducer, class Result>
|
||||
future<I> map_reduce_cf_raw(http_context& ctx, const sstring& name, I init,
|
||||
Mapper mapper, Reducer reducer, Result result) {
|
||||
auto uuid = get_uuid(name, ctx.db.local());
|
||||
return ctx.db.map_reduce0([mapper, uuid](database& db) {
|
||||
return mapper(db.find_column_family(uuid));
|
||||
}, init, reducer);
|
||||
}
|
||||
|
||||
|
||||
template<class Mapper, class I, class Reducer, class Result>
|
||||
future<json::json_return_type> map_reduce_cf(http_context& ctx, const sstring& name, I init,
|
||||
Mapper mapper, Reducer reducer, Result result) {
|
||||
return map_reduce_cf_raw(ctx, name, init, mapper, reducer).then([result](const I& res) mutable {
|
||||
return map_reduce_cf_raw(ctx, name, init, mapper, reducer, result).then([result](const I& res) mutable {
|
||||
result = res;
|
||||
return make_ready_future<json::json_return_type>(result);
|
||||
});
|
||||
}
|
||||
|
||||
struct map_reduce_column_families_locally {
|
||||
std::any init;
|
||||
std::function<std::any (column_family&)> mapper;
|
||||
std::function<std::any (std::any, std::any)> reducer;
|
||||
future<std::any> operator()(database& db) const {
|
||||
auto res = seastar::make_lw_shared<std::any>(init);
|
||||
return do_for_each(db.get_column_families(), [res, this](const std::pair<utils::UUID, seastar::lw_shared_ptr<table>>& i) {
|
||||
*res = reducer(*res.get(), mapper(*i.second.get()));
|
||||
}).then([res] {
|
||||
return *res;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template<class Mapper, class I, class Reducer>
|
||||
future<I> map_reduce_cf_raw(http_context& ctx, I init,
|
||||
Mapper mapper, Reducer reducer) {
|
||||
using mapper_type = std::function<std::any (column_family&)>;
|
||||
using reducer_type = std::function<std::any (std::any, std::any)>;
|
||||
auto wrapped_mapper = mapper_type([mapper = std::move(mapper)] (column_family& cf) mutable {
|
||||
return I(mapper(cf));
|
||||
});
|
||||
auto wrapped_reducer = reducer_type([reducer = std::move(reducer)] (std::any a, std::any b) mutable {
|
||||
return I(reducer(std::any_cast<I>(std::move(a)), std::any_cast<I>(std::move(b))));
|
||||
});
|
||||
return ctx.db.map_reduce0(map_reduce_column_families_locally{init, std::move(wrapped_mapper), wrapped_reducer}, std::any(init), wrapped_reducer).then([] (std::any res) {
|
||||
return std::any_cast<I>(std::move(res));
|
||||
});
|
||||
return ctx.db.map_reduce0([mapper, init, reducer](database& db) {
|
||||
auto res = init;
|
||||
for (auto i : db.get_column_families()) {
|
||||
res = reducer(res, mapper(*i.second.get()));
|
||||
}
|
||||
return res;
|
||||
}, init, reducer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,16 +22,15 @@
|
||||
#include "commitlog.hh"
|
||||
#include <db/commitlog/commitlog.hh>
|
||||
#include "api/api-doc/commitlog.json.hh"
|
||||
#include "database.hh"
|
||||
#include <vector>
|
||||
|
||||
namespace api {
|
||||
|
||||
template<typename T>
|
||||
static auto acquire_cl_metric(http_context& ctx, std::function<T (db::commitlog*)> func) {
|
||||
typedef T ret_type;
|
||||
template<typename Func>
|
||||
static auto acquire_cl_metric(http_context& ctx, Func&& func) {
|
||||
typedef std::result_of_t<Func(db::commitlog *)> ret_type;
|
||||
|
||||
return ctx.db.map_reduce0([func = std::move(func)](database& db) {
|
||||
return ctx.db.map_reduce0([func = std::forward<Func>(func)](database& db) {
|
||||
if (db.commitlog() == nullptr) {
|
||||
return make_ready_future<ret_type>();
|
||||
}
|
||||
@@ -64,15 +63,15 @@ void set_commitlog(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
httpd::commitlog_json::get_completed_tasks.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
return acquire_cl_metric<uint64_t>(ctx, std::bind(&db::commitlog::get_completed_tasks, std::placeholders::_1));
|
||||
return acquire_cl_metric(ctx, std::bind(&db::commitlog::get_completed_tasks, std::placeholders::_1));
|
||||
});
|
||||
|
||||
httpd::commitlog_json::get_pending_tasks.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
return acquire_cl_metric<uint64_t>(ctx, std::bind(&db::commitlog::get_pending_tasks, std::placeholders::_1));
|
||||
return acquire_cl_metric(ctx, std::bind(&db::commitlog::get_pending_tasks, std::placeholders::_1));
|
||||
});
|
||||
|
||||
httpd::commitlog_json::get_total_commit_log_size.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
return acquire_cl_metric<uint64_t>(ctx, std::bind(&db::commitlog::get_total_size, std::placeholders::_1));
|
||||
return acquire_cl_metric(ctx, std::bind(&db::commitlog::get_total_size, std::placeholders::_1));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ void set_compaction_manager(http_context& ctx, routes& r) {
|
||||
|
||||
for (const auto& c : cm.get_compactions()) {
|
||||
cm::summary s;
|
||||
s.ks = c->ks_name;
|
||||
s.cf = c->cf_name;
|
||||
s.ks = c->ks;
|
||||
s.cf = c->cf;
|
||||
s.unit = "keys";
|
||||
s.task_type = sstables::compaction_name(c->type);
|
||||
s.completed = c->total_keys_written;
|
||||
@@ -103,37 +103,29 @@ void set_compaction_manager(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
cm::get_compaction_history.set(r, [] (std::unique_ptr<request> req) {
|
||||
std::function<future<>(output_stream<char>&&)> f = [](output_stream<char>&& s) {
|
||||
return do_with(output_stream<char>(std::move(s)), true, [] (output_stream<char>& s, bool& first){
|
||||
return s.write("[").then([&s, &first] {
|
||||
return db::system_keyspace::get_compaction_history([&s, &first](const db::system_keyspace::compaction_history_entry& entry) mutable {
|
||||
cm::history h;
|
||||
h.id = entry.id.to_sstring();
|
||||
h.ks = std::move(entry.ks);
|
||||
h.cf = std::move(entry.cf);
|
||||
h.compacted_at = entry.compacted_at;
|
||||
h.bytes_in = entry.bytes_in;
|
||||
h.bytes_out = entry.bytes_out;
|
||||
for (auto it : entry.rows_merged) {
|
||||
httpd::compaction_manager_json::row_merged e;
|
||||
e.key = it.first;
|
||||
e.value = it.second;
|
||||
h.rows_merged.push(std::move(e));
|
||||
}
|
||||
auto fut = first ? make_ready_future<>() : s.write(", ");
|
||||
first = false;
|
||||
return fut.then([&s, h = std::move(h)] {
|
||||
return formatter::write(s, h);
|
||||
});
|
||||
}).then([&s] {
|
||||
return s.write("]").then([&s] {
|
||||
return s.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
return make_ready_future<json::json_return_type>(std::move(f));
|
||||
return db::system_keyspace::get_compaction_history().then([] (std::vector<db::system_keyspace::compaction_history_entry> history) {
|
||||
std::vector<cm::history> res;
|
||||
res.reserve(history.size());
|
||||
|
||||
for (auto& entry : history) {
|
||||
cm::history h;
|
||||
h.id = entry.id.to_sstring();
|
||||
h.ks = std::move(entry.ks);
|
||||
h.cf = std::move(entry.cf);
|
||||
h.compacted_at = entry.compacted_at;
|
||||
h.bytes_in = entry.bytes_in;
|
||||
h.bytes_out = entry.bytes_out;
|
||||
for (auto it : entry.rows_merged) {
|
||||
httpd::compaction_manager_json::row_merged e;
|
||||
e.key = it.first;
|
||||
e.value = it.second;
|
||||
h.rows_merged.push(std::move(e));
|
||||
}
|
||||
res.push_back(std::move(h));
|
||||
}
|
||||
|
||||
return make_ready_future<json::json_return_type>(res);
|
||||
});
|
||||
});
|
||||
|
||||
cm::get_compaction_info.set(r, [] (std::unique_ptr<request> req) {
|
||||
|
||||
113
api/config.cc
113
api/config.cc
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "api/config.hh"
|
||||
#include "api/api-doc/config.json.hh"
|
||||
#include "db/config.hh"
|
||||
#include "database.hh"
|
||||
#include <sstream>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
namespace api {
|
||||
|
||||
template<class T>
|
||||
json::json_return_type get_json_return_type(const T& val) {
|
||||
return json::json_return_type(val);
|
||||
}
|
||||
|
||||
/*
|
||||
* As commented on db::seed_provider_type is not used
|
||||
* and probably never will.
|
||||
*
|
||||
* Just in case, we will return its name
|
||||
*/
|
||||
template<>
|
||||
json::json_return_type get_json_return_type(const db::seed_provider_type& val) {
|
||||
return json::json_return_type(val.class_name);
|
||||
}
|
||||
|
||||
std::string format_type(const std::string& type) {
|
||||
if (type == "int") {
|
||||
return "integer";
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
future<> get_config_swagger_entry(const std::string& name, const std::string& description, const std::string& type, bool& first, output_stream<char>& os) {
|
||||
std::stringstream ss;
|
||||
if (first) {
|
||||
first=false;
|
||||
} else {
|
||||
ss <<',';
|
||||
};
|
||||
ss << "\"/config/" << name <<"\": {"
|
||||
"\"get\": {"
|
||||
"\"description\": \"" << boost::replace_all_copy(boost::replace_all_copy(boost::replace_all_copy(description,"\n","\\n"),"\"", "''"), "\t", " ") <<"\","
|
||||
"\"operationId\": \"find_config_"<< name <<"\","
|
||||
"\"produces\": ["
|
||||
"\"application/json\""
|
||||
"],"
|
||||
"\"tags\": [\"config\"],"
|
||||
"\"parameters\": ["
|
||||
"],"
|
||||
"\"responses\": {"
|
||||
"\"200\": {"
|
||||
"\"description\": \"Config value\","
|
||||
"\"schema\": {"
|
||||
"\"type\": \"" << format_type(type) << "\""
|
||||
"}"
|
||||
"},"
|
||||
"\"default\": {"
|
||||
"\"description\": \"unexpected error\","
|
||||
"\"schema\": {"
|
||||
"\"$ref\": \"#/definitions/ErrorModel\""
|
||||
"}"
|
||||
"}"
|
||||
"}"
|
||||
"}"
|
||||
"}";
|
||||
return os.write(ss.str());
|
||||
}
|
||||
|
||||
namespace cs = httpd::config_json;
|
||||
#define _get_config_value(name, type, deflt, status, desc, ...) if (id == #name) {return get_json_return_type(ctx.db.local().get_config().name());}
|
||||
|
||||
|
||||
#define _get_config_description(name, type, deflt, status, desc, ...) f = f.then([&os, &first] {return get_config_swagger_entry(#name, desc, #type, first, os);});
|
||||
|
||||
void set_config(std::shared_ptr < api_registry_builder20 > rb, http_context& ctx, routes& r) {
|
||||
rb->register_function(r, [] (output_stream<char>& os) {
|
||||
return do_with(true, [&os] (bool& first) {
|
||||
auto f = make_ready_future();
|
||||
_make_config_values(_get_config_description)
|
||||
return f;
|
||||
});
|
||||
});
|
||||
|
||||
cs::find_config_id.set(r, [&ctx] (const_req r) {
|
||||
auto id = r.param["id"];
|
||||
_make_config_values(_get_config_value)
|
||||
throw bad_param_exception(sstring("No such config entry: ") + id);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api.hh"
|
||||
#include <seastar/http/api_docs.hh>
|
||||
|
||||
namespace api {
|
||||
|
||||
void set_config(std::shared_ptr<api_registry_builder20> rb, http_context& ctx, routes& r);
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "api/lsa.hh"
|
||||
#include "api/api.hh"
|
||||
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "http/exception.hh"
|
||||
#include "utils/logalloc.hh"
|
||||
#include "log.hh"
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "messaging_service.hh"
|
||||
#include "message/messaging_service.hh"
|
||||
#include <seastar/rpc/rpc_types.hh>
|
||||
#include "rpc/rpc_types.hh"
|
||||
#include "api/api-doc/messaging_service.json.hh"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@@ -139,7 +139,7 @@ void set_messaging_service(http_context& ctx, routes& r) {
|
||||
messaging_verb v = i; // for type safety we use messaging_verb values
|
||||
auto idx = static_cast<uint32_t>(v);
|
||||
if (idx >= map->size()) {
|
||||
throw std::runtime_error(format("verb index out of bounds: {:d}, map size: {:d}", idx, map->size()));
|
||||
throw std::runtime_error(sprint("verb index out of bounds: %lu, map size: %lu", idx, map->size()));
|
||||
}
|
||||
if ((*map)[idx] > 0) {
|
||||
c.count = (*map)[idx];
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "service/storage_service.hh"
|
||||
#include "db/config.hh"
|
||||
#include "utils/histogram.hh"
|
||||
#include "database.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
|
||||
@@ -28,17 +28,13 @@
|
||||
#include <db/commitlog/commitlog.hh>
|
||||
#include <gms/gossiper.hh>
|
||||
#include <db/system_keyspace.hh>
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "http/exception.hh"
|
||||
#include "repair/repair.hh"
|
||||
#include "locator/snitch_base.hh"
|
||||
#include "column_family.hh"
|
||||
#include "log.hh"
|
||||
#include "release.hh"
|
||||
#include "sstables/compaction_manager.hh"
|
||||
#include "sstables/sstables.hh"
|
||||
#include "database.hh"
|
||||
|
||||
sstables::sstable::version_types get_highest_supported_format();
|
||||
|
||||
namespace api {
|
||||
|
||||
@@ -76,36 +72,21 @@ static std::vector<ss::token_range> describe_ring(const sstring& keyspace) {
|
||||
}
|
||||
|
||||
void set_storage_service(http_context& ctx, routes& r) {
|
||||
using ks_cf_func = std::function<future<json::json_return_type>(std::unique_ptr<request>, sstring, std::vector<sstring>)>;
|
||||
|
||||
auto wrap_ks_cf = [&ctx](ks_cf_func f) {
|
||||
return [&ctx, f = std::move(f)](std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto column_families = split_cf(req->get_query_param("cf"));
|
||||
if (column_families.empty()) {
|
||||
column_families = map_keys(ctx.db.local().find_keyspace(keyspace).metadata().get()->cf_meta_data());
|
||||
}
|
||||
return f(std::move(req), std::move(keyspace), std::move(column_families));
|
||||
};
|
||||
};
|
||||
|
||||
ss::local_hostid.set(r, [](std::unique_ptr<request> req) {
|
||||
return db::system_keyspace::get_local_host_id().then([](const utils::UUID& id) {
|
||||
return make_ready_future<json::json_return_type>(id.to_sstring());
|
||||
});
|
||||
});
|
||||
|
||||
ss::get_tokens.set(r, [] (std::unique_ptr<request> req) {
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(service::get_local_storage_service().get_token_metadata().sorted_tokens(), [](const dht::token& i) {
|
||||
return boost::lexical_cast<std::string>(i);
|
||||
}));
|
||||
ss::get_tokens.set(r, [] (const_req req) {
|
||||
auto tokens = service::get_local_storage_service().get_token_metadata().sorted_tokens();
|
||||
return container_to_vec(tokens);
|
||||
});
|
||||
|
||||
ss::get_node_tokens.set(r, [] (std::unique_ptr<request> req) {
|
||||
gms::inet_address addr(req->param["endpoint"]);
|
||||
return make_ready_future<json::json_return_type>(stream_range_as_array(service::get_local_storage_service().get_token_metadata().get_tokens(addr), [](const dht::token& i) {
|
||||
return boost::lexical_cast<std::string>(i);
|
||||
}));
|
||||
ss::get_node_tokens.set(r, [] (const_req req) {
|
||||
gms::inet_address addr(req.param["endpoint"]);
|
||||
auto tokens = service::get_local_storage_service().get_token_metadata().get_tokens(addr);
|
||||
return container_to_vec(tokens);
|
||||
});
|
||||
|
||||
ss::get_commitlog.set(r, [&ctx](const_req req) {
|
||||
@@ -126,7 +107,11 @@ void set_storage_service(http_context& ctx, routes& r) {
|
||||
});
|
||||
|
||||
ss::get_moving_nodes.set(r, [](const_req req) {
|
||||
auto points = service::get_local_storage_service().get_token_metadata().get_moving_endpoints();
|
||||
std::unordered_set<sstring> addr;
|
||||
for (auto i: points) {
|
||||
addr.insert(boost::lexical_cast<std::string>(i.second));
|
||||
}
|
||||
return container_to_vec(addr);
|
||||
});
|
||||
|
||||
@@ -316,44 +301,24 @@ void set_storage_service(http_context& ctx, routes& r) {
|
||||
});
|
||||
});
|
||||
|
||||
ss::scrub.set(r, wrap_ks_cf([&ctx](std::unique_ptr<request> req, sstring keyspace, std::vector<sstring> column_families) {
|
||||
// TODO: respect this
|
||||
ss::scrub.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto column_family = req->get_query_param("cf");
|
||||
auto disable_snapshot = req->get_query_param("disable_snapshot");
|
||||
auto skip_corrupted = req->get_query_param("skip_corrupted");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
auto f = make_ready_future<>();
|
||||
if (!req_param<bool>(*req, "disable_snapshot", false)) {
|
||||
auto tag = format("pre-scrub-{:d}", db_clock::now().time_since_epoch().count());
|
||||
f = parallel_for_each(column_families, [keyspace, tag](sstring cf) {
|
||||
return service::get_local_storage_service().take_column_family_snapshot(keyspace, cf, tag);
|
||||
});
|
||||
}
|
||||
|
||||
return f.then([&ctx, keyspace, column_families] {
|
||||
return ctx.db.invoke_on_all([=] (database& db) {
|
||||
return do_for_each(column_families, [=, &db](sstring cfname) {
|
||||
auto& cm = db.get_compaction_manager();
|
||||
auto& cf = db.find_column_family(keyspace, cfname);
|
||||
return cm.perform_sstable_scrub(&cf);
|
||||
});
|
||||
});
|
||||
}).then([]{
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
}));
|
||||
|
||||
ss::upgrade_sstables.set(r, wrap_ks_cf([&ctx](std::unique_ptr<request> req, sstring keyspace, std::vector<sstring> column_families) {
|
||||
bool exclude_current_version = req_param<bool>(*req, "exclude_current_version", false);
|
||||
|
||||
return ctx.db.invoke_on_all([=] (database& db) {
|
||||
return do_for_each(column_families, [=, &db](sstring cfname) {
|
||||
auto& cm = db.get_compaction_manager();
|
||||
auto& cf = db.find_column_family(keyspace, cfname);
|
||||
return cm.perform_sstable_upgrade(&cf, exclude_current_version);
|
||||
});
|
||||
}).then([]{
|
||||
return make_ready_future<json::json_return_type>(0);
|
||||
});
|
||||
}));
|
||||
ss::upgrade_sstables.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
//TBD
|
||||
unimplemented();
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto column_family = req->get_query_param("cf");
|
||||
auto exclude_current_version = req->get_query_param("exclude_current_version");
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
|
||||
ss::force_keyspace_flush.set(r, [&ctx](std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
@@ -491,7 +456,7 @@ void set_storage_service(http_context& ctx, routes& r) {
|
||||
return service::get_storage_service().map_reduce(adder<service::storage_service::drain_progress>(), [] (auto& ss) {
|
||||
return ss.get_drain_progress();
|
||||
}).then([] (auto&& progress) {
|
||||
auto progress_str = format("Drained {}/{} ColumnFamilies", progress.remaining_cfs, progress.total_cfs);
|
||||
auto progress_str = sprint("Drained %s/%s ColumnFamilies", progress.remaining_cfs, progress.total_cfs);
|
||||
return make_ready_future<json::json_return_type>(std::move(progress_str));
|
||||
});
|
||||
});
|
||||
@@ -702,11 +667,7 @@ void set_storage_service(http_context& ctx, routes& r) {
|
||||
auto coordinator = std::hash<sstring>()(cf) % smp::count;
|
||||
return service::get_storage_service().invoke_on(coordinator, [ks = std::move(ks), cf = std::move(cf)] (service::storage_service& s) {
|
||||
return s.load_new_sstables(ks, cf);
|
||||
}).then_wrapped([] (auto&& f) {
|
||||
if (f.failed()) {
|
||||
auto msg = fmt::format("Failed to load new sstables: {}", f.get_exception());
|
||||
return make_exception_future<json::json_return_type>(httpd::server_error_exception(msg));
|
||||
}
|
||||
}).then([] {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
});
|
||||
@@ -740,7 +701,7 @@ void set_storage_service(http_context& ctx, routes& r) {
|
||||
} catch (std::out_of_range& e) {
|
||||
throw httpd::bad_param_exception(e.what());
|
||||
} catch (std::invalid_argument&){
|
||||
throw httpd::bad_param_exception(format("Bad format in a probability value: \"{}\"", probability.c_str()));
|
||||
throw httpd::bad_param_exception(sprint("Bad format in a probability value: \"%s\"", probability.c_str()));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -776,7 +737,7 @@ void set_storage_service(http_context& ctx, routes& r) {
|
||||
return make_ready_future<json::json_return_type>(json_void());
|
||||
});
|
||||
} catch (...) {
|
||||
throw httpd::bad_param_exception(format("Bad format value: "));
|
||||
throw httpd::bad_param_exception(sprint("Bad format value: "));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -891,15 +852,6 @@ void set_storage_service(http_context& ctx, routes& r) {
|
||||
return make_ready_future<json::json_return_type>(map_to_key_value(ownership, res));
|
||||
});
|
||||
});
|
||||
|
||||
ss::view_build_statuses.set(r, [&ctx] (std::unique_ptr<request> req) {
|
||||
auto keyspace = validate_keyspace(ctx, req->param);
|
||||
auto view = req->param["view"];
|
||||
return service::get_local_storage_service().view_build_statuses(std::move(keyspace), std::move(view)).then([] (std::unordered_map<sstring, sstring> status) {
|
||||
std::vector<storage_service_json::mapper> res;
|
||||
return make_ready_future<json::json_return_type>(map_to_key_value(std::move(status), res));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "api/api-doc/system.json.hh"
|
||||
#include "api/api.hh"
|
||||
|
||||
#include <seastar/http/exception.hh>
|
||||
#include "http/exception.hh"
|
||||
#include "log.hh"
|
||||
|
||||
namespace api {
|
||||
|
||||
261
atomic_cell.cc
261
atomic_cell.cc
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "atomic_cell.hh"
|
||||
#include "atomic_cell_or_collection.hh"
|
||||
#include "types.hh"
|
||||
#include "types/collection.hh"
|
||||
|
||||
/// LSA mirator for cells with irrelevant type
|
||||
///
|
||||
///
|
||||
const data::type_imr_descriptor& no_type_imr_descriptor() {
|
||||
static thread_local data::type_imr_descriptor state(data::type_info::make_variable_size());
|
||||
return state;
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_dead(api::timestamp_type timestamp, gc_clock::time_point deletion_time) {
|
||||
auto& imr_data = no_type_imr_descriptor();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_dead(timestamp, deletion_time), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, bytes_view value, atomic_cell::collection_member cm) {
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, ser::buffer_view<bytes_ostream::fragment_iterator> value, atomic_cell::collection_member cm) {
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, const fragmented_temporary_buffer::view& value, collection_member cm)
|
||||
{
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, bytes_view value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, atomic_cell::collection_member cm) {
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, expiry, ttl, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, ser::buffer_view<bytes_ostream::fragment_iterator> value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, atomic_cell::collection_member cm) {
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, expiry, ttl, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_type timestamp, const fragmented_temporary_buffer::view& value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member cm)
|
||||
{
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live(imr_data.type_info(), timestamp, value, expiry, ttl, bool(cm)), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live_counter_update(api::timestamp_type timestamp, int64_t value) {
|
||||
auto& imr_data = no_type_imr_descriptor();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live_counter_update(timestamp, value), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell atomic_cell::make_live_uninitialized(const abstract_type& type, api::timestamp_type timestamp, size_t size) {
|
||||
auto& imr_data = no_type_imr_descriptor();
|
||||
return atomic_cell(
|
||||
imr_data.type_info(),
|
||||
imr_object_type::make(data::cell::make_live_uninitialized(imr_data.type_info(), timestamp, size), &imr_data.lsa_migrator())
|
||||
);
|
||||
}
|
||||
|
||||
static imr::utils::object<data::cell::structure> copy_cell(const data::type_imr_descriptor& imr_data, const uint8_t* ptr)
|
||||
{
|
||||
using imr_object_type = imr::utils::object<data::cell::structure>;
|
||||
|
||||
// If the cell doesn't own any memory it is trivial and can be copied with
|
||||
// memcpy.
|
||||
auto f = data::cell::structure::get_member<data::cell::tags::flags>(ptr);
|
||||
if (!f.template get<data::cell::tags::external_data>()) {
|
||||
data::cell::context ctx(f, imr_data.type_info());
|
||||
// XXX: We may be better off storing the total cell size in memory. Measure!
|
||||
auto size = data::cell::structure::serialized_object_size(ptr, ctx);
|
||||
return imr_object_type::make_raw(size, [&] (uint8_t* dst) noexcept {
|
||||
std::copy_n(ptr, size, dst);
|
||||
}, &imr_data.lsa_migrator());
|
||||
}
|
||||
|
||||
return imr_object_type::make(data::cell::copy_fn(imr_data.type_info(), ptr), &imr_data.lsa_migrator());
|
||||
}
|
||||
|
||||
atomic_cell::atomic_cell(const abstract_type& type, atomic_cell_view other)
|
||||
: atomic_cell(type.imr_state().type_info(),
|
||||
copy_cell(type.imr_state(), other._view.raw_pointer()))
|
||||
{ }
|
||||
|
||||
atomic_cell_or_collection atomic_cell_or_collection::copy(const abstract_type& type) const {
|
||||
if (!_data.get()) {
|
||||
return atomic_cell_or_collection();
|
||||
}
|
||||
auto& imr_data = type.imr_state();
|
||||
return atomic_cell_or_collection(
|
||||
copy_cell(imr_data, _data.get())
|
||||
);
|
||||
}
|
||||
|
||||
atomic_cell_or_collection::atomic_cell_or_collection(const abstract_type& type, atomic_cell_view acv)
|
||||
: _data(copy_cell(type.imr_state(), acv._view.raw_pointer()))
|
||||
{
|
||||
}
|
||||
|
||||
static collection_mutation_view get_collection_mutation_view(const uint8_t* ptr)
|
||||
{
|
||||
auto f = data::cell::structure::get_member<data::cell::tags::flags>(ptr);
|
||||
auto ti = data::type_info::make_collection();
|
||||
data::cell::context ctx(f, ti);
|
||||
auto view = data::cell::structure::get_member<data::cell::tags::cell>(ptr).as<data::cell::tags::collection>(ctx);
|
||||
auto dv = data::cell::variable_value::make_view(view, f.get<data::cell::tags::external_data>());
|
||||
return collection_mutation_view { dv };
|
||||
}
|
||||
|
||||
collection_mutation_view atomic_cell_or_collection::as_collection_mutation() const {
|
||||
return get_collection_mutation_view(_data.get());
|
||||
}
|
||||
|
||||
collection_mutation::collection_mutation(const collection_type_impl& type, collection_mutation_view v)
|
||||
: _data(imr_object_type::make(data::cell::make_collection(v.data), &type.imr_state().lsa_migrator()))
|
||||
{
|
||||
}
|
||||
|
||||
collection_mutation::collection_mutation(const collection_type_impl& type, bytes_view v)
|
||||
: _data(imr_object_type::make(data::cell::make_collection(v), &type.imr_state().lsa_migrator()))
|
||||
{
|
||||
}
|
||||
|
||||
collection_mutation::operator collection_mutation_view() const
|
||||
{
|
||||
return get_collection_mutation_view(_data.get());
|
||||
}
|
||||
|
||||
bool atomic_cell_or_collection::equals(const abstract_type& type, const atomic_cell_or_collection& other) const
|
||||
{
|
||||
auto ptr_a = _data.get();
|
||||
auto ptr_b = other._data.get();
|
||||
|
||||
if (!ptr_a || !ptr_b) {
|
||||
return !ptr_a && !ptr_b;
|
||||
}
|
||||
|
||||
if (type.is_atomic()) {
|
||||
auto a = atomic_cell_view::from_bytes(type.imr_state().type_info(), _data);
|
||||
auto b = atomic_cell_view::from_bytes(type.imr_state().type_info(), other._data);
|
||||
if (a.timestamp() != b.timestamp()) {
|
||||
return false;
|
||||
}
|
||||
if (a.is_live() != b.is_live()) {
|
||||
return false;
|
||||
}
|
||||
if (a.is_live()) {
|
||||
if (a.is_counter_update() != b.is_counter_update()) {
|
||||
return false;
|
||||
}
|
||||
if (a.is_counter_update()) {
|
||||
return a.counter_update_value() == b.counter_update_value();
|
||||
}
|
||||
if (a.is_live_and_has_ttl() != b.is_live_and_has_ttl()) {
|
||||
return false;
|
||||
}
|
||||
if (a.is_live_and_has_ttl()) {
|
||||
if (a.ttl() != b.ttl() || a.expiry() != b.expiry()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return a.value() == b.value();
|
||||
}
|
||||
return a.deletion_time() == b.deletion_time();
|
||||
} else {
|
||||
return as_collection_mutation().data == other.as_collection_mutation().data;
|
||||
}
|
||||
}
|
||||
|
||||
size_t atomic_cell_or_collection::external_memory_usage(const abstract_type& t) const
|
||||
{
|
||||
if (!_data.get()) {
|
||||
return 0;
|
||||
}
|
||||
auto ctx = data::cell::context(_data.get(), t.imr_state().type_info());
|
||||
|
||||
auto view = data::cell::structure::make_view(_data.get(), ctx);
|
||||
auto flags = view.get<data::cell::tags::flags>();
|
||||
|
||||
size_t external_value_size = 0;
|
||||
if (flags.get<data::cell::tags::external_data>()) {
|
||||
if (flags.get<data::cell::tags::collection>()) {
|
||||
external_value_size = get_collection_mutation_view(_data.get()).data.size_bytes();
|
||||
} else {
|
||||
auto cell_view = data::cell::atomic_cell_view(t.imr_state().type_info(), view);
|
||||
external_value_size = cell_view.value_size();
|
||||
}
|
||||
// Add overhead of chunk headers. The last one is a special case.
|
||||
external_value_size += (external_value_size - 1) / data::cell::maximum_external_chunk_length * data::cell::external_chunk_overhead;
|
||||
external_value_size += data::cell::external_last_chunk_overhead;
|
||||
}
|
||||
return data::cell::structure::serialized_object_size(_data.get(), ctx)
|
||||
+ imr_object_type::size_overhead + external_value_size;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const atomic_cell_or_collection::printer& p) {
|
||||
if (!p._cell._data.get()) {
|
||||
return os << "{ null atomic_cell_or_collection }";
|
||||
}
|
||||
using dc = data::cell;
|
||||
os << "{ ";
|
||||
if (dc::structure::get_member<dc::tags::flags>(p._cell._data.get()).get<dc::tags::collection>()) {
|
||||
os << "collection ";
|
||||
auto cmv = p._cell.as_collection_mutation();
|
||||
os << to_hex(cmv.data.linearize());
|
||||
} else {
|
||||
os << p._cell.as_atomic_cell(p._cdef);
|
||||
}
|
||||
return os << " }";
|
||||
}
|
||||
373
atomic_cell.hh
373
atomic_cell.hh
@@ -26,55 +26,193 @@
|
||||
#include "tombstone.hh"
|
||||
#include "gc_clock.hh"
|
||||
#include "utils/managed_bytes.hh"
|
||||
#include <seastar/net//byteorder.hh>
|
||||
#include "net/byteorder.hh"
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <seastar/util/gcc6-concepts.hh>
|
||||
#include "data/cell.hh"
|
||||
#include "data/schema_info.hh"
|
||||
#include "imr/utils.hh"
|
||||
#include "utils/fragmented_temporary_buffer.hh"
|
||||
|
||||
#include "serializer.hh"
|
||||
template<typename T, typename Input>
|
||||
static inline
|
||||
void set_field(Input& v, unsigned offset, T val) {
|
||||
reinterpret_cast<net::packed<T>*>(v.begin() + offset)->raw = net::hton(val);
|
||||
}
|
||||
|
||||
class abstract_type;
|
||||
class collection_type_impl;
|
||||
template<typename T>
|
||||
static inline
|
||||
T get_field(const bytes_view& v, unsigned offset) {
|
||||
return net::ntoh(*reinterpret_cast<const net::packed<T>*>(v.begin() + offset));
|
||||
}
|
||||
|
||||
using atomic_cell_value_view = data::value_view;
|
||||
using atomic_cell_value_mutable_view = data::value_mutable_view;
|
||||
class atomic_cell_or_collection;
|
||||
|
||||
/// View of an atomic cell
|
||||
template<mutable_view is_mutable>
|
||||
class basic_atomic_cell_view {
|
||||
protected:
|
||||
data::cell::basic_atomic_cell_view<is_mutable> _view;
|
||||
friend class atomic_cell;
|
||||
/*
|
||||
* Represents atomic cell layout. Works on serialized form.
|
||||
*
|
||||
* Layout:
|
||||
*
|
||||
* <live> := <int8_t:flags><int64_t:timestamp>(<int32_t:expiry><int32_t:ttl>)?<value>
|
||||
* <dead> := <int8_t: 0><int64_t:timestamp><int32_t:deletion_time>
|
||||
*/
|
||||
class atomic_cell_type final {
|
||||
private:
|
||||
static constexpr int8_t LIVE_FLAG = 0x01;
|
||||
static constexpr int8_t EXPIRY_FLAG = 0x02; // When present, expiry field is present. Set only for live cells
|
||||
static constexpr int8_t COUNTER_UPDATE_FLAG = 0x08; // Cell is a counter update.
|
||||
static constexpr int8_t COUNTER_IN_PLACE_REVERT = 0x10;
|
||||
static constexpr unsigned flags_size = 1;
|
||||
static constexpr unsigned timestamp_offset = flags_size;
|
||||
static constexpr unsigned timestamp_size = 8;
|
||||
static constexpr unsigned expiry_offset = timestamp_offset + timestamp_size;
|
||||
static constexpr unsigned expiry_size = 4;
|
||||
static constexpr unsigned deletion_time_offset = timestamp_offset + timestamp_size;
|
||||
static constexpr unsigned deletion_time_size = 4;
|
||||
static constexpr unsigned ttl_offset = expiry_offset + expiry_size;
|
||||
static constexpr unsigned ttl_size = 4;
|
||||
friend class counter_cell_builder;
|
||||
private:
|
||||
static bool is_counter_update(bytes_view cell) {
|
||||
return cell[0] & COUNTER_UPDATE_FLAG;
|
||||
}
|
||||
static bool is_counter_in_place_revert_set(bytes_view cell) {
|
||||
return cell[0] & COUNTER_IN_PLACE_REVERT;
|
||||
}
|
||||
template<typename BytesContainer>
|
||||
static void set_counter_in_place_revert(BytesContainer& cell, bool flag) {
|
||||
cell[0] = (cell[0] & ~COUNTER_IN_PLACE_REVERT) | (flag * COUNTER_IN_PLACE_REVERT);
|
||||
}
|
||||
static bool is_live(const bytes_view& cell) {
|
||||
return cell[0] & LIVE_FLAG;
|
||||
}
|
||||
static bool is_live_and_has_ttl(const bytes_view& cell) {
|
||||
return cell[0] & EXPIRY_FLAG;
|
||||
}
|
||||
static bool is_dead(const bytes_view& cell) {
|
||||
return !is_live(cell);
|
||||
}
|
||||
// Can be called on live and dead cells
|
||||
static api::timestamp_type timestamp(const bytes_view& cell) {
|
||||
return get_field<api::timestamp_type>(cell, timestamp_offset);
|
||||
}
|
||||
template<typename BytesContainer>
|
||||
static void set_timestamp(BytesContainer& cell, api::timestamp_type ts) {
|
||||
set_field(cell, timestamp_offset, ts);
|
||||
}
|
||||
// Can be called on live cells only
|
||||
private:
|
||||
template<typename BytesView>
|
||||
static BytesView do_get_value(BytesView cell) {
|
||||
auto expiry_field_size = bool(cell[0] & EXPIRY_FLAG) * (expiry_size + ttl_size);
|
||||
auto value_offset = flags_size + timestamp_size + expiry_field_size;
|
||||
cell.remove_prefix(value_offset);
|
||||
return cell;
|
||||
}
|
||||
public:
|
||||
using pointer_type = std::conditional_t<is_mutable == mutable_view::no, const uint8_t*, uint8_t*>;
|
||||
static bytes_view value(bytes_view cell) {
|
||||
return do_get_value(cell);
|
||||
}
|
||||
static bytes_mutable_view value(bytes_mutable_view cell) {
|
||||
return do_get_value(cell);
|
||||
}
|
||||
// Can be called on live counter update cells only
|
||||
static int64_t counter_update_value(bytes_view cell) {
|
||||
return get_field<int64_t>(cell, flags_size + timestamp_size);
|
||||
}
|
||||
// Can be called only when is_dead() is true.
|
||||
static gc_clock::time_point deletion_time(const bytes_view& cell) {
|
||||
assert(is_dead(cell));
|
||||
return gc_clock::time_point(gc_clock::duration(
|
||||
get_field<int32_t>(cell, deletion_time_offset)));
|
||||
}
|
||||
// Can be called only when is_live_and_has_ttl() is true.
|
||||
static gc_clock::time_point expiry(const bytes_view& cell) {
|
||||
assert(is_live_and_has_ttl(cell));
|
||||
auto expiry = get_field<int32_t>(cell, expiry_offset);
|
||||
return gc_clock::time_point(gc_clock::duration(expiry));
|
||||
}
|
||||
// Can be called only when is_live_and_has_ttl() is true.
|
||||
static gc_clock::duration ttl(const bytes_view& cell) {
|
||||
assert(is_live_and_has_ttl(cell));
|
||||
return gc_clock::duration(get_field<int32_t>(cell, ttl_offset));
|
||||
}
|
||||
static managed_bytes make_dead(api::timestamp_type timestamp, gc_clock::time_point deletion_time) {
|
||||
managed_bytes b(managed_bytes::initialized_later(), flags_size + timestamp_size + deletion_time_size);
|
||||
b[0] = 0;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
set_field(b, deletion_time_offset, deletion_time.time_since_epoch().count());
|
||||
return b;
|
||||
}
|
||||
static managed_bytes make_live(api::timestamp_type timestamp, bytes_view value) {
|
||||
auto value_offset = flags_size + timestamp_size;
|
||||
managed_bytes b(managed_bytes::initialized_later(), value_offset + value.size());
|
||||
b[0] = LIVE_FLAG;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
std::copy_n(value.begin(), value.size(), b.begin() + value_offset);
|
||||
return b;
|
||||
}
|
||||
static managed_bytes make_live_counter_update(api::timestamp_type timestamp, int64_t value) {
|
||||
auto value_offset = flags_size + timestamp_size;
|
||||
managed_bytes b(managed_bytes::initialized_later(), value_offset + sizeof(value));
|
||||
b[0] = LIVE_FLAG | COUNTER_UPDATE_FLAG;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
set_field(b, value_offset, value);
|
||||
return b;
|
||||
}
|
||||
static managed_bytes make_live(api::timestamp_type timestamp, bytes_view value, gc_clock::time_point expiry, gc_clock::duration ttl) {
|
||||
auto value_offset = flags_size + timestamp_size + expiry_size + ttl_size;
|
||||
managed_bytes b(managed_bytes::initialized_later(), value_offset + value.size());
|
||||
b[0] = EXPIRY_FLAG | LIVE_FLAG;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
set_field(b, expiry_offset, expiry.time_since_epoch().count());
|
||||
set_field(b, ttl_offset, ttl.count());
|
||||
std::copy_n(value.begin(), value.size(), b.begin() + value_offset);
|
||||
return b;
|
||||
}
|
||||
// make_live_from_serializer() is intended for users that need to serialise
|
||||
// some object or objects to the format used in atomic_cell::value().
|
||||
// With just make_live() the patter would look like follows:
|
||||
// 1. allocate a buffer and write to it serialised objects
|
||||
// 2. pass that buffer to make_live()
|
||||
// 3. make_live() needs to prepend some metadata to the cell value so it
|
||||
// allocates a new buffer and copies the content of the original one
|
||||
//
|
||||
// The allocation and copy of a buffer can be avoided.
|
||||
// make_live_from_serializer() allows the user code to specify the timestamp
|
||||
// and size of the cell value as well as provide the serialiser function
|
||||
// object, which would write the serialised value of the cell to the buffer
|
||||
// given to it by make_live_from_serializer().
|
||||
template<typename Serializer>
|
||||
GCC6_CONCEPT(requires requires(Serializer serializer, bytes::iterator it) {
|
||||
serializer(it);
|
||||
})
|
||||
static managed_bytes make_live_from_serializer(api::timestamp_type timestamp, size_t size, Serializer&& serializer) {
|
||||
auto value_offset = flags_size + timestamp_size;
|
||||
managed_bytes b(managed_bytes::initialized_later(), value_offset + size);
|
||||
b[0] = LIVE_FLAG;
|
||||
set_field(b, timestamp_offset, timestamp);
|
||||
serializer(b.begin() + value_offset);
|
||||
return b;
|
||||
}
|
||||
template<typename ByteContainer>
|
||||
friend class atomic_cell_base;
|
||||
friend class atomic_cell;
|
||||
};
|
||||
|
||||
template<typename ByteContainer>
|
||||
class atomic_cell_base {
|
||||
protected:
|
||||
explicit basic_atomic_cell_view(data::cell::basic_atomic_cell_view<is_mutable> v)
|
||||
: _view(std::move(v)) { }
|
||||
|
||||
basic_atomic_cell_view(const data::type_info& ti, pointer_type ptr)
|
||||
: _view(data::cell::make_atomic_cell_view(ti, ptr))
|
||||
{ }
|
||||
|
||||
ByteContainer _data;
|
||||
protected:
|
||||
atomic_cell_base(ByteContainer&& data) : _data(std::forward<ByteContainer>(data)) { }
|
||||
friend class atomic_cell_or_collection;
|
||||
public:
|
||||
operator basic_atomic_cell_view<mutable_view::no>() const noexcept {
|
||||
return basic_atomic_cell_view<mutable_view::no>(_view);
|
||||
}
|
||||
|
||||
void swap(basic_atomic_cell_view& other) noexcept {
|
||||
using std::swap;
|
||||
swap(_view, other._view);
|
||||
}
|
||||
|
||||
bool is_counter_update() const {
|
||||
return _view.is_counter_update();
|
||||
return atomic_cell_type::is_counter_update(_data);
|
||||
}
|
||||
bool is_counter_in_place_revert_set() const {
|
||||
return atomic_cell_type::is_counter_in_place_revert_set(_data);
|
||||
}
|
||||
bool is_live() const {
|
||||
return _view.is_live();
|
||||
return atomic_cell_type::is_live(_data);
|
||||
}
|
||||
bool is_live(tombstone t, bool is_counter) const {
|
||||
return is_live() && !is_covered_by(t, is_counter);
|
||||
@@ -83,140 +221,122 @@ public:
|
||||
return is_live() && !is_covered_by(t, is_counter) && !has_expired(now);
|
||||
}
|
||||
bool is_live_and_has_ttl() const {
|
||||
return _view.is_expiring();
|
||||
return atomic_cell_type::is_live_and_has_ttl(_data);
|
||||
}
|
||||
bool is_dead(gc_clock::time_point now) const {
|
||||
return !is_live() || has_expired(now);
|
||||
return atomic_cell_type::is_dead(_data) || has_expired(now);
|
||||
}
|
||||
bool is_covered_by(tombstone t, bool is_counter) const {
|
||||
return timestamp() <= t.timestamp || (is_counter && t.timestamp != api::missing_timestamp);
|
||||
}
|
||||
// Can be called on live and dead cells
|
||||
api::timestamp_type timestamp() const {
|
||||
return _view.timestamp();
|
||||
return atomic_cell_type::timestamp(_data);
|
||||
}
|
||||
void set_timestamp(api::timestamp_type ts) {
|
||||
_view.set_timestamp(ts);
|
||||
atomic_cell_type::set_timestamp(_data, ts);
|
||||
}
|
||||
// Can be called on live cells only
|
||||
data::basic_value_view<is_mutable> value() const {
|
||||
return _view.value();
|
||||
}
|
||||
// Can be called on live cells only
|
||||
size_t value_size() const {
|
||||
return _view.value_size();
|
||||
}
|
||||
bool is_value_fragmented() const {
|
||||
return _view.is_value_fragmented();
|
||||
auto value() const {
|
||||
return atomic_cell_type::value(_data);
|
||||
}
|
||||
// Can be called on live counter update cells only
|
||||
int64_t counter_update_value() const {
|
||||
return _view.counter_update_value();
|
||||
return atomic_cell_type::counter_update_value(_data);
|
||||
}
|
||||
// Can be called only when is_dead(gc_clock::time_point)
|
||||
gc_clock::time_point deletion_time() const {
|
||||
return !is_live() ? _view.deletion_time() : expiry() - ttl();
|
||||
return !is_live() ? atomic_cell_type::deletion_time(_data) : expiry() - ttl();
|
||||
}
|
||||
// Can be called only when is_live_and_has_ttl()
|
||||
gc_clock::time_point expiry() const {
|
||||
return _view.expiry();
|
||||
return atomic_cell_type::expiry(_data);
|
||||
}
|
||||
// Can be called only when is_live_and_has_ttl()
|
||||
gc_clock::duration ttl() const {
|
||||
return _view.ttl();
|
||||
return atomic_cell_type::ttl(_data);
|
||||
}
|
||||
// Can be called on live and dead cells
|
||||
bool has_expired(gc_clock::time_point now) const {
|
||||
return is_live_and_has_ttl() && expiry() <= now;
|
||||
}
|
||||
|
||||
bytes_view serialize() const {
|
||||
return _view.serialize();
|
||||
return _data;
|
||||
}
|
||||
void set_counter_in_place_revert(bool flag) {
|
||||
atomic_cell_type::set_counter_in_place_revert(_data, flag);
|
||||
}
|
||||
};
|
||||
|
||||
class atomic_cell_view final : public basic_atomic_cell_view<mutable_view::no> {
|
||||
atomic_cell_view(const data::type_info& ti, const uint8_t* data)
|
||||
: basic_atomic_cell_view<mutable_view::no>(ti, data) {}
|
||||
|
||||
template<mutable_view is_mutable>
|
||||
atomic_cell_view(data::cell::basic_atomic_cell_view<is_mutable> view)
|
||||
: basic_atomic_cell_view<mutable_view::no>(view) { }
|
||||
friend class atomic_cell;
|
||||
class atomic_cell_view final : public atomic_cell_base<bytes_view> {
|
||||
atomic_cell_view(bytes_view data) : atomic_cell_base(std::move(data)) {}
|
||||
public:
|
||||
static atomic_cell_view from_bytes(const data::type_info& ti, const imr::utils::object<data::cell::structure>& data) {
|
||||
return atomic_cell_view(ti, data.get());
|
||||
}
|
||||
|
||||
static atomic_cell_view from_bytes(const data::type_info& ti, bytes_view bv) {
|
||||
return atomic_cell_view(ti, reinterpret_cast<const uint8_t*>(bv.begin()));
|
||||
}
|
||||
static atomic_cell_view from_bytes(bytes_view data) { return atomic_cell_view(data); }
|
||||
|
||||
friend class atomic_cell;
|
||||
friend std::ostream& operator<<(std::ostream& os, const atomic_cell_view& acv);
|
||||
};
|
||||
|
||||
class atomic_cell_mutable_view final : public basic_atomic_cell_view<mutable_view::yes> {
|
||||
atomic_cell_mutable_view(const data::type_info& ti, uint8_t* data)
|
||||
: basic_atomic_cell_view<mutable_view::yes>(ti, data) {}
|
||||
class atomic_cell_mutable_view final : public atomic_cell_base<bytes_mutable_view> {
|
||||
atomic_cell_mutable_view(bytes_mutable_view data) : atomic_cell_base(std::move(data)) {}
|
||||
public:
|
||||
static atomic_cell_mutable_view from_bytes(const data::type_info& ti, imr::utils::object<data::cell::structure>& data) {
|
||||
return atomic_cell_mutable_view(ti, data.get());
|
||||
}
|
||||
static atomic_cell_mutable_view from_bytes(bytes_mutable_view data) { return atomic_cell_mutable_view(data); }
|
||||
|
||||
friend class atomic_cell;
|
||||
};
|
||||
|
||||
using atomic_cell_ref = atomic_cell_mutable_view;
|
||||
|
||||
class atomic_cell final : public basic_atomic_cell_view<mutable_view::yes> {
|
||||
using imr_object_type = imr::utils::object<data::cell::structure>;
|
||||
imr_object_type _data;
|
||||
atomic_cell(const data::type_info& ti, imr::utils::object<data::cell::structure>&& data)
|
||||
: basic_atomic_cell_view<mutable_view::yes>(ti, data.get()), _data(std::move(data)) {}
|
||||
class atomic_cell_ref final : public atomic_cell_base<managed_bytes&> {
|
||||
public:
|
||||
class collection_member_tag;
|
||||
using collection_member = bool_class<collection_member_tag>;
|
||||
atomic_cell_ref(managed_bytes& buf) : atomic_cell_base(buf) {}
|
||||
};
|
||||
|
||||
class atomic_cell final : public atomic_cell_base<managed_bytes> {
|
||||
atomic_cell(managed_bytes b) : atomic_cell_base(std::move(b)) {}
|
||||
public:
|
||||
atomic_cell(const atomic_cell&) = default;
|
||||
atomic_cell(atomic_cell&&) = default;
|
||||
atomic_cell& operator=(const atomic_cell&) = delete;
|
||||
atomic_cell& operator=(const atomic_cell&) = default;
|
||||
atomic_cell& operator=(atomic_cell&&) = default;
|
||||
void swap(atomic_cell& other) noexcept {
|
||||
basic_atomic_cell_view<mutable_view::yes>::swap(other);
|
||||
_data.swap(other._data);
|
||||
static atomic_cell from_bytes(managed_bytes b) {
|
||||
return atomic_cell(std::move(b));
|
||||
}
|
||||
operator atomic_cell_view() const { return atomic_cell_view(_view); }
|
||||
atomic_cell(const abstract_type& t, atomic_cell_view other);
|
||||
static atomic_cell make_dead(api::timestamp_type timestamp, gc_clock::time_point deletion_time);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, bytes_view value,
|
||||
collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, ser::buffer_view<bytes_ostream::fragment_iterator> value,
|
||||
collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, const fragmented_temporary_buffer::view& value,
|
||||
collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, const bytes& value,
|
||||
collection_member cm = collection_member::no) {
|
||||
return make_live(type, timestamp, bytes_view(value), cm);
|
||||
atomic_cell(atomic_cell_view other) : atomic_cell_base(managed_bytes{other._data}) {}
|
||||
operator atomic_cell_view() const {
|
||||
return atomic_cell_view(_data);
|
||||
}
|
||||
static atomic_cell make_live_counter_update(api::timestamp_type timestamp, int64_t value);
|
||||
static atomic_cell make_live(const abstract_type&, api::timestamp_type timestamp, bytes_view value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type&, api::timestamp_type timestamp, ser::buffer_view<bytes_ostream::fragment_iterator> value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type&, api::timestamp_type timestamp, const fragmented_temporary_buffer::view& value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member = collection_member::no);
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, const bytes& value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl, collection_member cm = collection_member::no)
|
||||
static atomic_cell make_dead(api::timestamp_type timestamp, gc_clock::time_point deletion_time) {
|
||||
return atomic_cell_type::make_dead(timestamp, deletion_time);
|
||||
}
|
||||
static atomic_cell make_live(api::timestamp_type timestamp, bytes_view value) {
|
||||
return atomic_cell_type::make_live(timestamp, value);
|
||||
}
|
||||
static atomic_cell make_live(api::timestamp_type timestamp, const bytes& value) {
|
||||
return make_live(timestamp, bytes_view(value));
|
||||
}
|
||||
static atomic_cell make_live_counter_update(api::timestamp_type timestamp, int64_t value) {
|
||||
return atomic_cell_type::make_live_counter_update(timestamp, value);
|
||||
}
|
||||
static atomic_cell make_live(api::timestamp_type timestamp, bytes_view value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl)
|
||||
{
|
||||
return make_live(type, timestamp, bytes_view(value), expiry, ttl, cm);
|
||||
return atomic_cell_type::make_live(timestamp, value, expiry, ttl);
|
||||
}
|
||||
static atomic_cell make_live(const abstract_type& type, api::timestamp_type timestamp, bytes_view value, ttl_opt ttl, collection_member cm = collection_member::no) {
|
||||
static atomic_cell make_live(api::timestamp_type timestamp, const bytes& value,
|
||||
gc_clock::time_point expiry, gc_clock::duration ttl)
|
||||
{
|
||||
return make_live(timestamp, bytes_view(value), expiry, ttl);
|
||||
}
|
||||
static atomic_cell make_live(api::timestamp_type timestamp, bytes_view value, ttl_opt ttl) {
|
||||
if (!ttl) {
|
||||
return make_live(type, timestamp, value, cm);
|
||||
return atomic_cell_type::make_live(timestamp, value);
|
||||
} else {
|
||||
return make_live(type, timestamp, value, gc_clock::now() + *ttl, *ttl, cm);
|
||||
return atomic_cell_type::make_live(timestamp, value, gc_clock::now() + *ttl, *ttl);
|
||||
}
|
||||
}
|
||||
static atomic_cell make_live_uninitialized(const abstract_type& type, api::timestamp_type timestamp, size_t size);
|
||||
template<typename Serializer>
|
||||
static atomic_cell make_live_from_serializer(api::timestamp_type timestamp, size_t size, Serializer&& serializer) {
|
||||
return atomic_cell_type::make_live_from_serializer(timestamp, size, std::forward<Serializer>(serializer));
|
||||
}
|
||||
friend class atomic_cell_or_collection;
|
||||
friend std::ostream& operator<<(std::ostream& os, const atomic_cell& ac);
|
||||
};
|
||||
@@ -230,24 +350,33 @@ class collection_mutation_view;
|
||||
// list: tbd, probably ugly
|
||||
class collection_mutation {
|
||||
public:
|
||||
using imr_object_type = imr::utils::object<data::cell::structure>;
|
||||
imr_object_type _data;
|
||||
|
||||
managed_bytes data;
|
||||
collection_mutation() {}
|
||||
collection_mutation(const collection_type_impl&, collection_mutation_view v);
|
||||
collection_mutation(const collection_type_impl&, bytes_view bv);
|
||||
collection_mutation(managed_bytes b) : data(std::move(b)) {}
|
||||
collection_mutation(collection_mutation_view v);
|
||||
operator collection_mutation_view() const;
|
||||
};
|
||||
|
||||
|
||||
class collection_mutation_view {
|
||||
public:
|
||||
atomic_cell_value_view data;
|
||||
bytes_view data;
|
||||
bytes_view serialize() const { return data; }
|
||||
static collection_mutation_view from_bytes(bytes_view v) { return { v }; }
|
||||
};
|
||||
|
||||
inline
|
||||
collection_mutation::collection_mutation(collection_mutation_view v)
|
||||
: data(v.data) {
|
||||
}
|
||||
|
||||
inline
|
||||
collection_mutation::operator collection_mutation_view() const {
|
||||
return { data };
|
||||
}
|
||||
|
||||
class column_definition;
|
||||
|
||||
int compare_atomic_cell_for_merge(atomic_cell_view left, atomic_cell_view right);
|
||||
void merge_column(const abstract_type& def,
|
||||
void merge_column(const column_definition& def,
|
||||
atomic_cell_or_collection& old,
|
||||
const atomic_cell_or_collection& neww);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
// Not part of atomic_cell.hh to avoid cyclic dependency between types.hh and atomic_cell.hh
|
||||
|
||||
#include "types.hh"
|
||||
#include "types/collection.hh"
|
||||
#include "atomic_cell.hh"
|
||||
#include "atomic_cell_or_collection.hh"
|
||||
#include "hashing.hh"
|
||||
@@ -34,15 +33,12 @@ template<>
|
||||
struct appending_hash<collection_mutation_view> {
|
||||
template<typename Hasher>
|
||||
void operator()(Hasher& h, collection_mutation_view cell, const column_definition& cdef) const {
|
||||
cell.data.with_linearized([&] (bytes_view cell_bv) {
|
||||
auto ctype = static_pointer_cast<const collection_type_impl>(cdef.type);
|
||||
auto m_view = ctype->deserialize_mutation_form(cell_bv);
|
||||
auto m_view = collection_type_impl::deserialize_mutation_form(cell);
|
||||
::feed_hash(h, m_view.tomb);
|
||||
for (auto&& key_and_value : m_view.cells) {
|
||||
::feed_hash(h, key_and_value.first);
|
||||
::feed_hash(h, key_and_value.second, cdef);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,9 +50,7 @@ struct appending_hash<atomic_cell_view> {
|
||||
feed_hash(h, cell.timestamp());
|
||||
if (cell.is_live()) {
|
||||
if (cdef.is_counter()) {
|
||||
counter_cell_view::with_linearized(cell, [&] (counter_cell_view ccv) {
|
||||
::feed_hash(h, ccv);
|
||||
});
|
||||
::feed_hash(h, counter_cell_view(cell));
|
||||
return;
|
||||
}
|
||||
if (cell.is_live_and_has_ttl()) {
|
||||
@@ -91,9 +85,9 @@ struct appending_hash<atomic_cell_or_collection> {
|
||||
template<typename Hasher>
|
||||
void operator()(Hasher& h, const atomic_cell_or_collection& c, const column_definition& cdef) const {
|
||||
if (cdef.is_atomic()) {
|
||||
feed_hash(h, c.as_atomic_cell(cdef), cdef);
|
||||
feed_hash(h, c.as_atomic_cell(), cdef);
|
||||
} else {
|
||||
feed_hash(h, c.as_collection_mutation(), cdef);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -25,68 +25,42 @@
|
||||
#include "schema.hh"
|
||||
#include "hashing.hh"
|
||||
|
||||
#include "imr/utils.hh"
|
||||
|
||||
// A variant type that can hold either an atomic_cell, or a serialized collection.
|
||||
// Which type is stored is determined by the schema.
|
||||
// Has an "empty" state.
|
||||
// Objects moved-from are left in an empty state.
|
||||
class atomic_cell_or_collection final {
|
||||
// FIXME: This has made us lose small-buffer optimisation. Unfortunately,
|
||||
// due to the changed cell format it would be less effective now, anyway.
|
||||
// Measure the actual impact because any attempts to fix this will become
|
||||
// irrelevant once rows are converted to the IMR as well, so maybe we can
|
||||
// live with this like that.
|
||||
using imr_object_type = imr::utils::object<data::cell::structure>;
|
||||
imr_object_type _data;
|
||||
managed_bytes _data;
|
||||
private:
|
||||
atomic_cell_or_collection(imr::utils::object<data::cell::structure>&& data) : _data(std::move(data)) {}
|
||||
atomic_cell_or_collection(managed_bytes&& data) : _data(std::move(data)) {}
|
||||
public:
|
||||
atomic_cell_or_collection() = default;
|
||||
atomic_cell_or_collection(atomic_cell_or_collection&&) = default;
|
||||
atomic_cell_or_collection(const atomic_cell_or_collection&) = delete;
|
||||
atomic_cell_or_collection& operator=(atomic_cell_or_collection&&) = default;
|
||||
atomic_cell_or_collection& operator=(const atomic_cell_or_collection&) = delete;
|
||||
atomic_cell_or_collection(atomic_cell ac) : _data(std::move(ac._data)) {}
|
||||
atomic_cell_or_collection(const abstract_type& at, atomic_cell_view acv);
|
||||
static atomic_cell_or_collection from_atomic_cell(atomic_cell data) { return { std::move(data._data) }; }
|
||||
atomic_cell_view as_atomic_cell(const column_definition& cdef) const { return atomic_cell_view::from_bytes(cdef.type->imr_state().type_info(), _data); }
|
||||
atomic_cell_ref as_atomic_cell_ref(const column_definition& cdef) { return atomic_cell_mutable_view::from_bytes(cdef.type->imr_state().type_info(), _data); }
|
||||
atomic_cell_mutable_view as_mutable_atomic_cell(const column_definition& cdef) { return atomic_cell_mutable_view::from_bytes(cdef.type->imr_state().type_info(), _data); }
|
||||
atomic_cell_or_collection(collection_mutation cm) : _data(std::move(cm._data)) { }
|
||||
atomic_cell_or_collection copy(const abstract_type&) const;
|
||||
atomic_cell_view as_atomic_cell() const { return atomic_cell_view::from_bytes(_data); }
|
||||
atomic_cell_ref as_atomic_cell_ref() { return { _data }; }
|
||||
atomic_cell_mutable_view as_mutable_atomic_cell() { return atomic_cell_mutable_view::from_bytes(_data); }
|
||||
atomic_cell_or_collection(collection_mutation cm) : _data(std::move(cm.data)) {}
|
||||
explicit operator bool() const {
|
||||
return bool(_data);
|
||||
return !_data.empty();
|
||||
}
|
||||
static constexpr bool can_use_mutable_view() {
|
||||
return true;
|
||||
bool can_use_mutable_view() const {
|
||||
return !_data.is_fragmented();
|
||||
}
|
||||
void swap(atomic_cell_or_collection& other) noexcept {
|
||||
_data.swap(other._data);
|
||||
static atomic_cell_or_collection from_collection_mutation(collection_mutation data) {
|
||||
return std::move(data.data);
|
||||
}
|
||||
static atomic_cell_or_collection from_collection_mutation(collection_mutation data) { return std::move(data._data); }
|
||||
collection_mutation_view as_collection_mutation() const;
|
||||
bytes_view serialize() const;
|
||||
bool equals(const abstract_type& type, const atomic_cell_or_collection& other) const;
|
||||
size_t external_memory_usage(const abstract_type&) const;
|
||||
|
||||
class printer {
|
||||
const column_definition& _cdef;
|
||||
const atomic_cell_or_collection& _cell;
|
||||
public:
|
||||
printer(const column_definition& cdef, const atomic_cell_or_collection& cell)
|
||||
: _cdef(cdef), _cell(cell) { }
|
||||
printer(const printer&) = delete;
|
||||
printer(printer&&) = delete;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream&, const printer&);
|
||||
};
|
||||
friend std::ostream& operator<<(std::ostream&, const printer&);
|
||||
collection_mutation_view as_collection_mutation() const {
|
||||
return collection_mutation_view{_data};
|
||||
}
|
||||
bytes_view serialize() const {
|
||||
return _data;
|
||||
}
|
||||
bool operator==(const atomic_cell_or_collection& other) const {
|
||||
return _data == other._data;
|
||||
}
|
||||
size_t external_memory_usage() const {
|
||||
return _data.external_memory_usage();
|
||||
}
|
||||
friend std::ostream& operator<<(std::ostream&, const atomic_cell_or_collection&);
|
||||
};
|
||||
|
||||
namespace std {
|
||||
|
||||
inline void swap(atomic_cell_or_collection& a, atomic_cell_or_collection& b) noexcept
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,19 +72,19 @@ public:
|
||||
return make_ready_future<authenticated_user>(anonymous_user());
|
||||
}
|
||||
|
||||
virtual future<> create(std::string_view, const authentication_options& options) const override {
|
||||
virtual future<> create(stdx::string_view, const authentication_options& options) const override {
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
virtual future<> alter(std::string_view, const authentication_options& options) const override {
|
||||
virtual future<> alter(stdx::string_view, const authentication_options& options) const override {
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
virtual future<> drop(std::string_view) const override {
|
||||
virtual future<> drop(stdx::string_view) const override {
|
||||
return make_ready_future();
|
||||
}
|
||||
|
||||
virtual future<custom_options> query_custom_options(std::string_view role_name) const override {
|
||||
virtual future<custom_options> query_custom_options(stdx::string_view role_name) const override {
|
||||
return make_ready_future<custom_options>();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "auth/authorizer.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace cql3 {
|
||||
class query_processor;
|
||||
@@ -57,12 +58,12 @@ public:
|
||||
return make_ready_future<permission_set>(permissions::ALL);
|
||||
}
|
||||
|
||||
virtual future<> grant(std::string_view, permission_set, const resource&) const override {
|
||||
virtual future<> grant(stdx::string_view, permission_set, const resource&) const override {
|
||||
return make_exception_future<>(
|
||||
unsupported_authorization_operation("GRANT operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
|
||||
virtual future<> revoke(std::string_view, permission_set, const resource&) const override {
|
||||
virtual future<> revoke(stdx::string_view, permission_set, const resource&) const override {
|
||||
return make_exception_future<>(
|
||||
unsupported_authorization_operation("REVOKE operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
@@ -73,7 +74,7 @@ public:
|
||||
"LIST PERMISSIONS operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
|
||||
virtual future<> revoke_all(std::string_view) const override {
|
||||
virtual future<> revoke_all(stdx::string_view) const override {
|
||||
return make_exception_future(
|
||||
unsupported_authorization_operation("REVOKE operation is not supported by AllowAllAuthorizer"));
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
namespace auth {
|
||||
|
||||
authenticated_user::authenticated_user(std::string_view name)
|
||||
authenticated_user::authenticated_user(stdx::string_view name)
|
||||
: name(sstring(name)) {
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <optional>
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <seastar/core/sstring.hh>
|
||||
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -66,7 +67,7 @@ public:
|
||||
/// An anonymous user.
|
||||
///
|
||||
authenticated_user() = default;
|
||||
explicit authenticated_user(std::string_view name);
|
||||
explicit authenticated_user(stdx::string_view name);
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
@@ -57,7 +57,7 @@ inline bool any_authentication_options(const authentication_options& aos) noexce
|
||||
class unsupported_authentication_option : public std::invalid_argument {
|
||||
public:
|
||||
explicit unsupported_authentication_option(authentication_option k)
|
||||
: std::invalid_argument(format("The {} option is not supported.", k)) {
|
||||
: std::invalid_argument(sprint("The %s option is not supported.", k)) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "auth/common.hh"
|
||||
#include "auth/password_authenticator.hh"
|
||||
#include "cql3/query_processor.hh"
|
||||
#include "db/config.hh"
|
||||
#include "utils/class_registrator.hh"
|
||||
|
||||
const sstring auth::authenticator::USERNAME_KEY("username");
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
@@ -55,10 +55,10 @@
|
||||
|
||||
#include "auth/authentication_options.hh"
|
||||
#include "auth/resource.hh"
|
||||
#include "auth/sasl_challenge.hh"
|
||||
#include "bytes.hh"
|
||||
#include "enum_set.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace db {
|
||||
class config;
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
///
|
||||
/// The options provided must be a subset of `supported_options()`.
|
||||
///
|
||||
virtual future<> create(std::string_view role_name, const authentication_options& options) const = 0;
|
||||
virtual future<> create(stdx::string_view role_name, const authentication_options& options) const = 0;
|
||||
|
||||
///
|
||||
/// Alter the authentication record of an existing user.
|
||||
@@ -131,25 +131,39 @@ public:
|
||||
///
|
||||
/// Callers must ensure that the specification of `alterable_options()` is adhered to.
|
||||
///
|
||||
virtual future<> alter(std::string_view role_name, const authentication_options& options) const = 0;
|
||||
virtual future<> alter(stdx::string_view role_name, const authentication_options& options) const = 0;
|
||||
|
||||
///
|
||||
/// Delete the authentication record for a user. This will disallow the user from logging in.
|
||||
///
|
||||
virtual future<> drop(std::string_view role_name) const = 0;
|
||||
virtual future<> drop(stdx::string_view role_name) const = 0;
|
||||
|
||||
///
|
||||
/// Query for custom options (those corresponding to \ref authentication_options::options).
|
||||
///
|
||||
/// If no options are set the result is an empty container.
|
||||
///
|
||||
virtual future<custom_options> query_custom_options(std::string_view role_name) const = 0;
|
||||
virtual future<custom_options> query_custom_options(stdx::string_view role_name) const = 0;
|
||||
|
||||
///
|
||||
/// System resources used internally as part of the implementation. These are made inaccessible to users.
|
||||
///
|
||||
virtual const resource_set& protected_resources() const = 0;
|
||||
|
||||
///
|
||||
/// A stateful SASL challenge which supports many authentication schemes (depending on the implementation).
|
||||
///
|
||||
class sasl_challenge {
|
||||
public:
|
||||
virtual ~sasl_challenge() = default;
|
||||
|
||||
virtual bytes evaluate_response(bytes_view client_response) = 0;
|
||||
|
||||
virtual bool is_complete() const = 0;
|
||||
|
||||
virtual future<authenticated_user> get_authenticated_user() const = 0;
|
||||
};
|
||||
|
||||
virtual ::shared_ptr<sasl_challenge> new_sasl_challenge() const = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "auth/permission.hh"
|
||||
#include "auth/resource.hh"
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -116,14 +117,14 @@ public:
|
||||
///
|
||||
/// \throws \ref unsupported_authorization_operation if granting permissions is not supported.
|
||||
///
|
||||
virtual future<> grant(std::string_view role_name, permission_set, const resource&) const = 0;
|
||||
virtual future<> grant(stdx::string_view role_name, permission_set, const resource&) const = 0;
|
||||
|
||||
///
|
||||
/// Revoke a set of permissions from a role for a particular \ref resource.
|
||||
///
|
||||
/// \throws \ref unsupported_authorization_operation if revoking permissions is not supported.
|
||||
///
|
||||
virtual future<> revoke(std::string_view role_name, permission_set, const resource&) const = 0;
|
||||
virtual future<> revoke(stdx::string_view role_name, permission_set, const resource&) const = 0;
|
||||
|
||||
///
|
||||
/// Query for all directly granted permissions.
|
||||
@@ -137,7 +138,7 @@ public:
|
||||
///
|
||||
/// \throws \ref unsupported_authorization_operation if revoking permissions is not supported.
|
||||
///
|
||||
virtual future<> revoke_all(std::string_view role_name) const = 0;
|
||||
virtual future<> revoke_all(stdx::string_view role_name) const = 0;
|
||||
|
||||
///
|
||||
/// Revoke all permissions granted to any role for a particular resource.
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "database.hh"
|
||||
#include "schema_builder.hh"
|
||||
#include "service/migration_manager.hh"
|
||||
#include "timeout_config.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -48,9 +47,9 @@ future<> do_after_system_ready(seastar::abort_source& as, seastar::noncopyable_f
|
||||
struct empty_state { };
|
||||
return delay_until_system_ready(as).then([&as, func = std::move(func)] () mutable {
|
||||
return exponential_backoff_retry::do_until_value(1s, 1min, as, [func = std::move(func)] {
|
||||
return func().then_wrapped([] (auto&& f) -> std::optional<empty_state> {
|
||||
return func().then_wrapped([] (auto&& f) -> stdx::optional<empty_state> {
|
||||
if (f.failed()) {
|
||||
auth_log.debug("Auth task failed with error, rescheduling: {}", f.get_exception());
|
||||
auth_log.info("Auth task failed with error, rescheduling: {}", f.get_exception());
|
||||
return { };
|
||||
}
|
||||
return { empty_state() };
|
||||
@@ -60,14 +59,16 @@ future<> do_after_system_ready(seastar::abort_source& as, seastar::noncopyable_f
|
||||
}
|
||||
|
||||
future<> create_metadata_table_if_missing(
|
||||
std::string_view table_name,
|
||||
stdx::string_view table_name,
|
||||
cql3::query_processor& qp,
|
||||
std::string_view cql,
|
||||
stdx::string_view cql,
|
||||
::service::migration_manager& mm) {
|
||||
static auto ignore_existing = [] (seastar::noncopyable_function<future<>()> func) {
|
||||
return futurize_apply(std::move(func)).handle_exception_type([] (exceptions::already_exists_exception& ignored) { });
|
||||
};
|
||||
auto& db = qp.db();
|
||||
auto& db = qp.db().local();
|
||||
|
||||
if (db.has_schema(meta::AUTH_KS, sstring(table_name))) {
|
||||
return make_ready_future<>();
|
||||
}
|
||||
|
||||
auto parsed_statement = static_pointer_cast<cql3::statements::raw::cf_statement>(
|
||||
cql3::query_processor::parse_statement(cql));
|
||||
|
||||
@@ -76,36 +77,21 @@ future<> create_metadata_table_if_missing(
|
||||
auto statement = static_pointer_cast<cql3::statements::create_table_statement>(
|
||||
parsed_statement->prepare(db, qp.get_cql_stats())->statement);
|
||||
|
||||
const auto schema = statement->get_cf_meta_data(qp.db());
|
||||
const auto schema = statement->get_cf_meta_data(qp.db().local());
|
||||
const auto uuid = generate_legacy_id(schema->ks_name(), schema->cf_name());
|
||||
|
||||
schema_builder b(schema);
|
||||
b.set_uuid(uuid);
|
||||
schema_ptr table = b.build();
|
||||
return ignore_existing([&mm, table = std::move(table)] () {
|
||||
return mm.announce_new_column_family(table, false);
|
||||
});
|
||||
|
||||
return mm.announce_new_column_family(b.build(), false);
|
||||
}
|
||||
|
||||
future<> wait_for_schema_agreement(::service::migration_manager& mm, const database& db, seastar::abort_source& as) {
|
||||
future<> wait_for_schema_agreement(::service::migration_manager& mm, const database& db) {
|
||||
static const auto pause = [] { return sleep(std::chrono::milliseconds(500)); };
|
||||
|
||||
return do_until([&db, &as] {
|
||||
as.check();
|
||||
return db.get_version() != database::empty_version;
|
||||
}, pause).then([&mm, &as] {
|
||||
return do_until([&mm, &as] {
|
||||
as.check();
|
||||
return mm.have_schema_agreement();
|
||||
}, pause);
|
||||
return do_until([&db] { return db.get_version() != database::empty_version; }, pause).then([&mm] {
|
||||
return do_until([&mm] { return mm.have_schema_agreement(); }, pause);
|
||||
});
|
||||
}
|
||||
|
||||
const timeout_config& internal_distributed_timeout_config() noexcept {
|
||||
static const auto t = 5s;
|
||||
static const timeout_config tc{t, t, t, t, t, t, t};
|
||||
return tc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
|
||||
#include <seastar/core/future.hh>
|
||||
#include <seastar/core/abort_source.hh>
|
||||
@@ -38,7 +38,6 @@
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class database;
|
||||
class timeout_config;
|
||||
|
||||
namespace service {
|
||||
class migration_manager;
|
||||
@@ -76,16 +75,11 @@ inline future<> delay_until_system_ready(seastar::abort_source& as) {
|
||||
future<> do_after_system_ready(seastar::abort_source& as, seastar::noncopyable_function<future<>()> func);
|
||||
|
||||
future<> create_metadata_table_if_missing(
|
||||
std::string_view table_name,
|
||||
stdx::string_view table_name,
|
||||
cql3::query_processor&,
|
||||
std::string_view cql,
|
||||
stdx::string_view cql,
|
||||
::service::migration_manager&);
|
||||
|
||||
future<> wait_for_schema_agreement(::service::migration_manager&, const database&, seastar::abort_source&);
|
||||
|
||||
///
|
||||
/// Time-outs for internal, non-local CQL queries.
|
||||
///
|
||||
const timeout_config& internal_distributed_timeout_config() noexcept;
|
||||
future<> wait_for_schema_agreement(::service::migration_manager&, const database&);
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ extern "C" {
|
||||
#include "cql3/untyped_result_set.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "log.hh"
|
||||
#include "database.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -95,16 +94,15 @@ default_authorizer::~default_authorizer() {
|
||||
static const sstring legacy_table_name{"permissions"};
|
||||
|
||||
bool default_authorizer::legacy_metadata_exists() const {
|
||||
return _qp.db().has_schema(meta::AUTH_KS, legacy_table_name);
|
||||
return _qp.db().local().has_schema(meta::AUTH_KS, legacy_table_name);
|
||||
}
|
||||
|
||||
future<bool> default_authorizer::any_granted() const {
|
||||
static const sstring query = format("SELECT * FROM {}.{} LIMIT 1", meta::AUTH_KS, PERMISSIONS_CF);
|
||||
static const sstring query = sprint("SELECT * FROM %s.%s LIMIT 1", meta::AUTH_KS, PERMISSIONS_CF);
|
||||
|
||||
return _qp.process(
|
||||
query,
|
||||
db::consistency_level::LOCAL_ONE,
|
||||
infinite_timeout_config,
|
||||
{},
|
||||
true).then([this](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
return !results->empty();
|
||||
@@ -113,12 +111,11 @@ future<bool> default_authorizer::any_granted() const {
|
||||
|
||||
future<> default_authorizer::migrate_legacy_metadata() const {
|
||||
alogger.info("Starting migration of legacy permissions metadata.");
|
||||
static const sstring query = format("SELECT * FROM {}.{}", meta::AUTH_KS, legacy_table_name);
|
||||
static const sstring query = sprint("SELECT * FROM %s.%s", meta::AUTH_KS, legacy_table_name);
|
||||
|
||||
return _qp.process(
|
||||
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"),
|
||||
@@ -161,7 +158,7 @@ future<> default_authorizer::start() {
|
||||
_migration_manager).then([this] {
|
||||
_finished = do_after_system_ready(_as, [this] {
|
||||
return async([this] {
|
||||
wait_for_schema_agreement(_migration_manager, _qp.db(), _as).get0();
|
||||
wait_for_schema_agreement(_migration_manager, _qp.db().local()).get0();
|
||||
|
||||
if (legacy_metadata_exists()) {
|
||||
if (!any_granted().get0()) {
|
||||
@@ -179,7 +176,7 @@ future<> default_authorizer::start() {
|
||||
|
||||
future<> default_authorizer::stop() {
|
||||
_as.request_abort();
|
||||
return _finished.handle_exception_type([](const sleep_aborted&) {}).handle_exception_type([](const abort_requested_exception&) {});
|
||||
return _finished.handle_exception_type([](const sleep_aborted&) {});
|
||||
}
|
||||
|
||||
future<permission_set>
|
||||
@@ -188,7 +185,8 @@ default_authorizer::authorize(const role_or_anonymous& maybe_role, const resourc
|
||||
return make_ready_future<permission_set>(permissions::NONE);
|
||||
}
|
||||
|
||||
static const sstring query = format("SELECT {} FROM {}.{} WHERE {} = ? AND {} = ?",
|
||||
static const sstring query = sprint(
|
||||
"SELECT %s FROM %s.%s WHERE %s = ? AND %s = ?",
|
||||
PERMISSIONS_NAME,
|
||||
meta::AUTH_KS,
|
||||
PERMISSIONS_CF,
|
||||
@@ -198,7 +196,6 @@ default_authorizer::authorize(const role_or_anonymous& maybe_role, const resourc
|
||||
return _qp.process(
|
||||
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;
|
||||
@@ -210,12 +207,13 @@ default_authorizer::authorize(const role_or_anonymous& maybe_role, const resourc
|
||||
|
||||
future<>
|
||||
default_authorizer::modify(
|
||||
std::string_view role_name,
|
||||
stdx::string_view role_name,
|
||||
permission_set set,
|
||||
const resource& resource,
|
||||
std::string_view op) const {
|
||||
stdx::string_view op) const {
|
||||
return do_with(
|
||||
format("UPDATE {}.{} SET {} = {} {} ? WHERE {} = ? AND {} = ?",
|
||||
sprint(
|
||||
"UPDATE %s.%s SET %s = %s %s ? WHERE %s = ? AND %s = ?",
|
||||
meta::AUTH_KS,
|
||||
PERMISSIONS_CF,
|
||||
PERMISSIONS_NAME,
|
||||
@@ -227,22 +225,22 @@ default_authorizer::modify(
|
||||
return _qp.process(
|
||||
query,
|
||||
db::consistency_level::ONE,
|
||||
internal_distributed_timeout_config(),
|
||||
{permissions::to_strings(set), sstring(role_name), resource.name()}).discard_result();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
future<> default_authorizer::grant(std::string_view role_name, permission_set set, const resource& resource) const {
|
||||
future<> default_authorizer::grant(stdx::string_view role_name, permission_set set, const resource& resource) const {
|
||||
return modify(role_name, std::move(set), resource, "+");
|
||||
}
|
||||
|
||||
future<> default_authorizer::revoke(std::string_view role_name, permission_set set, const resource& resource) const {
|
||||
future<> default_authorizer::revoke(stdx::string_view role_name, permission_set set, const resource& resource) const {
|
||||
return modify(role_name, std::move(set), resource, "-");
|
||||
}
|
||||
|
||||
future<std::vector<permission_details>> default_authorizer::list_all() const {
|
||||
static const sstring query = format("SELECT {}, {}, {} FROM {}.{}",
|
||||
static const sstring query = sprint(
|
||||
"SELECT %s, %s, %s FROM %s.%s",
|
||||
ROLE_NAME,
|
||||
RESOURCE_NAME,
|
||||
PERMISSIONS_NAME,
|
||||
@@ -252,7 +250,6 @@ future<std::vector<permission_details>> default_authorizer::list_all() const {
|
||||
return _qp.process(
|
||||
query,
|
||||
db::consistency_level::ONE,
|
||||
internal_distributed_timeout_config(),
|
||||
{},
|
||||
true).then([](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
std::vector<permission_details> all_details;
|
||||
@@ -270,8 +267,9 @@ future<std::vector<permission_details>> default_authorizer::list_all() const {
|
||||
});
|
||||
}
|
||||
|
||||
future<> default_authorizer::revoke_all(std::string_view role_name) const {
|
||||
static const sstring query = format("DELETE FROM {}.{} WHERE {} = ?",
|
||||
future<> default_authorizer::revoke_all(stdx::string_view role_name) const {
|
||||
static const sstring query = sprint(
|
||||
"DELETE FROM %s.%s WHERE %s = ?",
|
||||
meta::AUTH_KS,
|
||||
PERMISSIONS_CF,
|
||||
ROLE_NAME);
|
||||
@@ -279,7 +277,6 @@ future<> default_authorizer::revoke_all(std::string_view role_name) const {
|
||||
return _qp.process(
|
||||
query,
|
||||
db::consistency_level::ONE,
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name)}).discard_result().handle_exception([role_name](auto ep) {
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
@@ -290,7 +287,8 @@ future<> default_authorizer::revoke_all(std::string_view role_name) const {
|
||||
}
|
||||
|
||||
future<> default_authorizer::revoke_all(const resource& resource) const {
|
||||
static const sstring query = format("SELECT {} FROM {}.{} WHERE {} = ? ALLOW FILTERING",
|
||||
static const sstring query = sprint(
|
||||
"SELECT %s FROM %s.%s WHERE %s = ? ALLOW FILTERING",
|
||||
ROLE_NAME,
|
||||
meta::AUTH_KS,
|
||||
PERMISSIONS_CF,
|
||||
@@ -299,7 +297,6 @@ future<> default_authorizer::revoke_all(const resource& resource) const {
|
||||
return _qp.process(
|
||||
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();
|
||||
@@ -307,7 +304,8 @@ future<> default_authorizer::revoke_all(const resource& resource) const {
|
||||
res->begin(),
|
||||
res->end(),
|
||||
[this, res, resource](const cql3::untyped_result_set::row& r) {
|
||||
static const sstring query = format("DELETE FROM {}.{} WHERE {} = ? AND {} = ?",
|
||||
static const sstring query = sprint(
|
||||
"DELETE FROM %s.%s WHERE %s = ? AND %s = ?",
|
||||
meta::AUTH_KS,
|
||||
PERMISSIONS_CF,
|
||||
ROLE_NAME,
|
||||
@@ -316,7 +314,6 @@ future<> default_authorizer::revoke_all(const resource& resource) const {
|
||||
return _qp.process(
|
||||
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 {
|
||||
|
||||
@@ -77,13 +77,13 @@ public:
|
||||
|
||||
virtual future<permission_set> authorize(const role_or_anonymous&, const resource&) const override;
|
||||
|
||||
virtual future<> grant(std::string_view, permission_set, const resource&) const override;
|
||||
virtual future<> grant(stdx::string_view, permission_set, const resource&) const override;
|
||||
|
||||
virtual future<> revoke( std::string_view, permission_set, const resource&) const override;
|
||||
virtual future<> revoke( stdx::string_view, permission_set, const resource&) const override;
|
||||
|
||||
virtual future<std::vector<permission_details>> list_all() const override;
|
||||
|
||||
virtual future<> revoke_all(std::string_view) const override;
|
||||
virtual future<> revoke_all(stdx::string_view) const override;
|
||||
|
||||
virtual future<> revoke_all(const resource&) const override;
|
||||
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
|
||||
future<> migrate_legacy_metadata() const;
|
||||
|
||||
future<> modify(std::string_view, permission_set, const resource&, std::string_view) const;
|
||||
future<> modify(stdx::string_view, permission_set, const resource&, stdx::string_view) const;
|
||||
};
|
||||
|
||||
} /* namespace auth */
|
||||
|
||||
@@ -41,24 +41,25 @@
|
||||
|
||||
#include "auth/password_authenticator.hh"
|
||||
|
||||
extern "C" {
|
||||
#include <crypt.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
|
||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||
#include <seastar/core/reactor.hh>
|
||||
|
||||
#include "auth/authenticated_user.hh"
|
||||
#include "auth/common.hh"
|
||||
#include "auth/passwords.hh"
|
||||
#include "auth/roles-metadata.hh"
|
||||
#include "cql3/untyped_result_set.hh"
|
||||
#include "log.hh"
|
||||
#include "service/migration_manager.hh"
|
||||
#include "utils/class_registrator.hh"
|
||||
#include "database.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -81,8 +82,6 @@ static const class_registrator<
|
||||
cql3::query_processor&,
|
||||
::service::migration_manager&> password_auth_reg("org.apache.cassandra.auth.PasswordAuthenticator");
|
||||
|
||||
static thread_local auto rng_for_salt = std::default_random_engine(std::random_device{}());
|
||||
|
||||
password_authenticator::~password_authenticator() {
|
||||
}
|
||||
|
||||
@@ -92,11 +91,84 @@ password_authenticator::password_authenticator(cql3::query_processor& qp, ::serv
|
||||
, _stopped(make_ready_future<>()) {
|
||||
}
|
||||
|
||||
static bool has_salted_hash(const cql3::untyped_result_set_row& row) {
|
||||
return !row.get_or<sstring>(SALTED_HASH, "").empty();
|
||||
// TODO: blowfish
|
||||
// Origin uses Java bcrypt library, i.e. blowfish salt
|
||||
// generation and hashing, which is arguably a "better"
|
||||
// password hash than sha/md5 versions usually available in
|
||||
// crypt_r. Otoh, glibc 2.7+ uses a modified sha512 algo
|
||||
// which should be the same order of safe, so the only
|
||||
// real issue should be salted hash compatibility with
|
||||
// origin if importing system tables from there.
|
||||
//
|
||||
// Since bcrypt/blowfish is _not_ (afaict) not available
|
||||
// as a dev package/lib on most linux distros, we'd have to
|
||||
// copy and compile for example OWL crypto
|
||||
// (http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/glibc/crypt_blowfish/)
|
||||
// to be fully bit-compatible.
|
||||
//
|
||||
// Until we decide this is needed, let's just use crypt_r,
|
||||
// and some old-fashioned random salt generation.
|
||||
|
||||
static constexpr size_t rand_bytes = 16;
|
||||
static thread_local crypt_data tlcrypt = { 0, };
|
||||
|
||||
static sstring hashpw(const sstring& pass, const sstring& salt) {
|
||||
auto res = crypt_r(pass.c_str(), salt.c_str(), &tlcrypt);
|
||||
if (res == nullptr) {
|
||||
throw std::system_error(errno, std::system_category());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static const sstring update_row_query = format("UPDATE {} SET {} = ? WHERE {} = ?",
|
||||
static bool checkpw(const sstring& pass, const sstring& salted_hash) {
|
||||
auto tmp = hashpw(pass, salted_hash);
|
||||
return tmp == salted_hash;
|
||||
}
|
||||
|
||||
static sstring gensalt() {
|
||||
static sstring prefix;
|
||||
|
||||
std::random_device rd;
|
||||
std::default_random_engine e1(rd());
|
||||
std::uniform_int_distribution<char> dist;
|
||||
|
||||
sstring valid_salt = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
|
||||
sstring input(rand_bytes, 0);
|
||||
|
||||
for (char&c : input) {
|
||||
c = valid_salt[dist(e1) % valid_salt.size()];
|
||||
}
|
||||
|
||||
sstring salt;
|
||||
|
||||
if (!prefix.empty()) {
|
||||
return prefix + input;
|
||||
}
|
||||
|
||||
// Try in order:
|
||||
// blowfish 2011 fix, blowfish, sha512, sha256, md5
|
||||
for (sstring pfx : { "$2y$", "$2a$", "$6$", "$5$", "$1$" }) {
|
||||
salt = pfx + input;
|
||||
const char* e = crypt_r("fisk", salt.c_str(), &tlcrypt);
|
||||
|
||||
if (e && (e[0] != '*')) {
|
||||
prefix = pfx;
|
||||
return salt;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Could not initialize hashing algorithm");
|
||||
}
|
||||
|
||||
static sstring hashpw(const sstring& pass) {
|
||||
return hashpw(pass, gensalt());
|
||||
}
|
||||
|
||||
static bool has_salted_hash(const cql3::untyped_result_set_row& row) {
|
||||
return utf8_type->deserialize(row.get_blob(SALTED_HASH)) != data_value::make_null(utf8_type);
|
||||
}
|
||||
|
||||
static const sstring update_row_query = sprint(
|
||||
"UPDATE %s SET %s = ? WHERE %s = ?",
|
||||
meta::roles_table::qualified_name(),
|
||||
SALTED_HASH,
|
||||
meta::roles_table::role_col_name);
|
||||
@@ -104,17 +176,16 @@ static const sstring update_row_query = format("UPDATE {} SET {} = ? WHERE {} =
|
||||
static const sstring legacy_table_name{"credentials"};
|
||||
|
||||
bool password_authenticator::legacy_metadata_exists() const {
|
||||
return _qp.db().has_schema(meta::AUTH_KS, legacy_table_name);
|
||||
return _qp.db().local().has_schema(meta::AUTH_KS, legacy_table_name);
|
||||
}
|
||||
|
||||
future<> password_authenticator::migrate_legacy_metadata() const {
|
||||
plogger.info("Starting migration of legacy authentication metadata.");
|
||||
static const sstring query = format("SELECT * FROM {}.{}", meta::AUTH_KS, legacy_table_name);
|
||||
static const sstring query = sprint("SELECT * FROM %s.%s", meta::AUTH_KS, legacy_table_name);
|
||||
|
||||
return _qp.process(
|
||||
query,
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_timeout_config()).then([this](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
db::consistency_level::QUORUM).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 +193,6 @@ future<> password_authenticator::migrate_legacy_metadata() const {
|
||||
return _qp.process(
|
||||
update_row_query,
|
||||
consistency_for_user(username),
|
||||
internal_distributed_timeout_config(),
|
||||
{std::move(salted_hash), username}).discard_result();
|
||||
}).finally([results] {});
|
||||
}).then([] {
|
||||
@@ -139,8 +209,7 @@ future<> password_authenticator::create_default_if_missing() const {
|
||||
return _qp.process(
|
||||
update_row_query,
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_timeout_config(),
|
||||
{passwords::hash(DEFAULT_USER_PASSWORD, rng_for_salt), DEFAULT_USER_NAME}).then([](auto&&) {
|
||||
{hashpw(DEFAULT_USER_PASSWORD), DEFAULT_USER_NAME}).then([](auto&&) {
|
||||
plogger.info("Created default superuser authentication record.");
|
||||
});
|
||||
}
|
||||
@@ -151,6 +220,8 @@ future<> password_authenticator::create_default_if_missing() const {
|
||||
|
||||
future<> password_authenticator::start() {
|
||||
return once_among_shards([this] {
|
||||
gensalt(); // do this once to determine usable hashing
|
||||
|
||||
auto f = create_metadata_table_if_missing(
|
||||
meta::roles_table::name,
|
||||
_qp,
|
||||
@@ -159,7 +230,7 @@ future<> password_authenticator::start() {
|
||||
|
||||
_stopped = do_after_system_ready(_as, [this] {
|
||||
return async([this] {
|
||||
wait_for_schema_agreement(_migration_manager, _qp.db(), _as).get0();
|
||||
wait_for_schema_agreement(_migration_manager, _qp.db().local()).get0();
|
||||
|
||||
if (any_nondefault_role_row_satisfies(_qp, &has_salted_hash).get0()) {
|
||||
if (legacy_metadata_exists()) {
|
||||
@@ -184,10 +255,10 @@ future<> password_authenticator::start() {
|
||||
|
||||
future<> password_authenticator::stop() {
|
||||
_as.request_abort();
|
||||
return _stopped.handle_exception_type([] (const sleep_aborted&) { }).handle_exception_type([](const abort_requested_exception&) {});
|
||||
return _stopped.handle_exception_type([] (const sleep_aborted&) { });
|
||||
}
|
||||
|
||||
db::consistency_level password_authenticator::consistency_for_user(std::string_view role_name) {
|
||||
db::consistency_level password_authenticator::consistency_for_user(stdx::string_view role_name) {
|
||||
if (role_name == DEFAULT_USER_NAME) {
|
||||
return db::consistency_level::QUORUM;
|
||||
}
|
||||
@@ -213,10 +284,10 @@ authentication_option_set password_authenticator::alterable_options() const {
|
||||
future<authenticated_user> password_authenticator::authenticate(
|
||||
const credentials_map& credentials) const {
|
||||
if (!credentials.count(USERNAME_KEY)) {
|
||||
throw exceptions::authentication_exception(format("Required key '{}' is missing", USERNAME_KEY));
|
||||
throw exceptions::authentication_exception(sprint("Required key '%s' is missing", USERNAME_KEY));
|
||||
}
|
||||
if (!credentials.count(PASSWORD_KEY)) {
|
||||
throw exceptions::authentication_exception(format("Required key '{}' is missing", PASSWORD_KEY));
|
||||
throw exceptions::authentication_exception(sprint("Required key '%s' is missing", PASSWORD_KEY));
|
||||
}
|
||||
|
||||
auto& username = credentials.at(USERNAME_KEY);
|
||||
@@ -228,7 +299,8 @@ future<authenticated_user> password_authenticator::authenticate(
|
||||
// Rely on query processing caching statements instead, and lets assume
|
||||
// that a map lookup string->statement is not gonna kill us much.
|
||||
return futurize_apply([this, username, password] {
|
||||
static const sstring query = format("SELECT {} FROM {} WHERE {} = ?",
|
||||
static const sstring query = sprint(
|
||||
"SELECT %s FROM %s WHERE %s = ?",
|
||||
SALTED_HASH,
|
||||
meta::roles_table::qualified_name(),
|
||||
meta::roles_table::role_col_name);
|
||||
@@ -236,17 +308,12 @@ future<authenticated_user> password_authenticator::authenticate(
|
||||
return _qp.process(
|
||||
query,
|
||||
consistency_for_user(username),
|
||||
internal_distributed_timeout_config(),
|
||||
{username},
|
||||
true);
|
||||
}).then_wrapped([=](future<::shared_ptr<cql3::untyped_result_set>> f) {
|
||||
try {
|
||||
auto res = f.get0();
|
||||
auto salted_hash = std::optional<sstring>();
|
||||
if (!res->empty()) {
|
||||
salted_hash = res->one().get_opt<sstring>(SALTED_HASH);
|
||||
}
|
||||
if (!salted_hash || !passwords::check(password, *salted_hash)) {
|
||||
if (res->empty() || !checkpw(password, res->one().get_as<sstring>(SALTED_HASH))) {
|
||||
throw exceptions::authentication_exception("Username and/or password are incorrect");
|
||||
}
|
||||
return make_ready_future<authenticated_user>(username);
|
||||
@@ -254,15 +321,13 @@ future<authenticated_user> password_authenticator::authenticate(
|
||||
std::throw_with_nested(exceptions::authentication_exception("Could not verify password"));
|
||||
} catch (exceptions::request_execution_exception& e) {
|
||||
std::throw_with_nested(exceptions::authentication_exception(e.what()));
|
||||
} catch (exceptions::authentication_exception& e) {
|
||||
std::throw_with_nested(e);
|
||||
} catch (...) {
|
||||
std::throw_with_nested(exceptions::authentication_exception("authentication failed"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
future<> password_authenticator::create(std::string_view role_name, const authentication_options& options) const {
|
||||
future<> password_authenticator::create(stdx::string_view role_name, const authentication_options& options) const {
|
||||
if (!options.password) {
|
||||
return make_ready_future<>();
|
||||
}
|
||||
@@ -270,16 +335,16 @@ future<> password_authenticator::create(std::string_view role_name, const authen
|
||||
return _qp.process(
|
||||
update_row_query,
|
||||
consistency_for_user(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{passwords::hash(*options.password, rng_for_salt), sstring(role_name)}).discard_result();
|
||||
{hashpw(*options.password), sstring(role_name)}).discard_result();
|
||||
}
|
||||
|
||||
future<> password_authenticator::alter(std::string_view role_name, const authentication_options& options) const {
|
||||
future<> password_authenticator::alter(stdx::string_view role_name, const authentication_options& options) const {
|
||||
if (!options.password) {
|
||||
return make_ready_future<>();
|
||||
}
|
||||
|
||||
static const sstring query = format("UPDATE {} SET {} = ? WHERE {} = ?",
|
||||
static const sstring query = sprint(
|
||||
"UPDATE %s SET %s = ? WHERE %s = ?",
|
||||
meta::roles_table::qualified_name(),
|
||||
SALTED_HASH,
|
||||
meta::roles_table::role_col_name);
|
||||
@@ -287,23 +352,20 @@ future<> password_authenticator::alter(std::string_view role_name, const authent
|
||||
return _qp.process(
|
||||
query,
|
||||
consistency_for_user(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{passwords::hash(*options.password, rng_for_salt), sstring(role_name)}).discard_result();
|
||||
{hashpw(*options.password), sstring(role_name)}).discard_result();
|
||||
}
|
||||
|
||||
future<> password_authenticator::drop(std::string_view name) const {
|
||||
static const sstring query = format("DELETE {} FROM {} WHERE {} = ?",
|
||||
future<> password_authenticator::drop(stdx::string_view name) const {
|
||||
static const sstring query = sprint(
|
||||
"DELETE %s FROM %s WHERE %s = ?",
|
||||
SALTED_HASH,
|
||||
meta::roles_table::qualified_name(),
|
||||
meta::roles_table::role_col_name);
|
||||
|
||||
return _qp.process(
|
||||
query, consistency_for_user(name),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(name)}).discard_result();
|
||||
return _qp.process(query, consistency_for_user(name), {sstring(name)}).discard_result();
|
||||
}
|
||||
|
||||
future<custom_options> password_authenticator::query_custom_options(std::string_view role_name) const {
|
||||
future<custom_options> password_authenticator::query_custom_options(stdx::string_view role_name) const {
|
||||
return make_ready_future<custom_options>();
|
||||
}
|
||||
|
||||
@@ -312,13 +374,75 @@ const resource_set& password_authenticator::protected_resources() const {
|
||||
return resources;
|
||||
}
|
||||
|
||||
::shared_ptr<sasl_challenge> password_authenticator::new_sasl_challenge() const {
|
||||
return ::make_shared<plain_sasl_challenge>([this](std::string_view username, std::string_view password) {
|
||||
credentials_map credentials{};
|
||||
credentials[USERNAME_KEY] = sstring(username);
|
||||
credentials[PASSWORD_KEY] = sstring(password);
|
||||
return this->authenticate(credentials);
|
||||
});
|
||||
::shared_ptr<authenticator::sasl_challenge> password_authenticator::new_sasl_challenge() const {
|
||||
class plain_text_password_challenge : public sasl_challenge {
|
||||
const password_authenticator& _self;
|
||||
|
||||
public:
|
||||
plain_text_password_challenge(const password_authenticator& self) : _self(self) {
|
||||
}
|
||||
|
||||
/**
|
||||
* SASL PLAIN mechanism specifies that credentials are encoded in a
|
||||
* sequence of UTF-8 bytes, delimited by 0 (US-ASCII NUL).
|
||||
* The form is : {code}authzId<NUL>authnId<NUL>password<NUL>{code}
|
||||
* authzId is optional, and in fact we don't care about it here as we'll
|
||||
* set the authzId to match the authnId (that is, there is no concept of
|
||||
* a user being authorized to act on behalf of another).
|
||||
*
|
||||
* @param bytes encoded credentials string sent by the client
|
||||
* @return map containing the username/password pairs in the form an IAuthenticator
|
||||
* would expect
|
||||
* @throws javax.security.sasl.SaslException
|
||||
*/
|
||||
bytes evaluate_response(bytes_view client_response) override {
|
||||
plogger.debug("Decoding credentials from client token");
|
||||
|
||||
sstring username, password;
|
||||
|
||||
auto b = client_response.crbegin();
|
||||
auto e = client_response.crend();
|
||||
auto i = b;
|
||||
|
||||
while (i != e) {
|
||||
if (*i == 0) {
|
||||
sstring tmp(i.base(), b.base());
|
||||
if (password.empty()) {
|
||||
password = std::move(tmp);
|
||||
} else if (username.empty()) {
|
||||
username = std::move(tmp);
|
||||
}
|
||||
b = ++i;
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if (username.empty()) {
|
||||
throw exceptions::authentication_exception("Authentication ID must not be null");
|
||||
}
|
||||
if (password.empty()) {
|
||||
throw exceptions::authentication_exception("Password must not be null");
|
||||
}
|
||||
|
||||
_credentials[USERNAME_KEY] = std::move(username);
|
||||
_credentials[PASSWORD_KEY] = std::move(password);
|
||||
_complete = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool is_complete() const override {
|
||||
return _complete;
|
||||
}
|
||||
|
||||
future<authenticated_user> get_authenticated_user() const override {
|
||||
return _self.authenticate(_credentials);
|
||||
}
|
||||
private:
|
||||
credentials_map _credentials;
|
||||
bool _complete = false;
|
||||
};
|
||||
return ::make_shared<plain_text_password_challenge>(*this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class password_authenticator : public authenticator {
|
||||
seastar::abort_source _as;
|
||||
|
||||
public:
|
||||
static db::consistency_level consistency_for_user(std::string_view role_name);
|
||||
static db::consistency_level consistency_for_user(stdx::string_view role_name);
|
||||
|
||||
password_authenticator(cql3::query_processor&, ::service::migration_manager&);
|
||||
|
||||
@@ -81,13 +81,13 @@ public:
|
||||
|
||||
virtual future<authenticated_user> authenticate(const credentials_map& credentials) const override;
|
||||
|
||||
virtual future<> create(std::string_view role_name, const authentication_options& options) const override;
|
||||
virtual future<> create(stdx::string_view role_name, const authentication_options& options) const override;
|
||||
|
||||
virtual future<> alter(std::string_view role_name, const authentication_options& options) const override;
|
||||
virtual future<> alter(stdx::string_view role_name, const authentication_options& options) const override;
|
||||
|
||||
virtual future<> drop(std::string_view role_name) const override;
|
||||
virtual future<> drop(stdx::string_view role_name) const override;
|
||||
|
||||
virtual future<custom_options> query_custom_options(std::string_view role_name) const override;
|
||||
virtual future<custom_options> query_custom_options(stdx::string_view role_name) const override;
|
||||
|
||||
virtual const resource_set& protected_resources() const override;
|
||||
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "auth/passwords.hh"
|
||||
|
||||
#include <cerrno>
|
||||
#include <optional>
|
||||
|
||||
extern "C" {
|
||||
#include <crypt.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
namespace auth::passwords {
|
||||
|
||||
static thread_local crypt_data tlcrypt = { 0, };
|
||||
|
||||
namespace detail {
|
||||
|
||||
scheme identify_best_supported_scheme() {
|
||||
const auto all_schemes = { scheme::bcrypt_y, scheme::bcrypt_a, scheme::sha_512, scheme::sha_256, scheme::md5 };
|
||||
// "Random", for testing schemes.
|
||||
const sstring random_part_of_salt = "aaaabbbbccccdddd";
|
||||
|
||||
for (scheme c : all_schemes) {
|
||||
const sstring salt = sstring(prefix_for_scheme(c)) + random_part_of_salt;
|
||||
const char* e = crypt_r("fisk", salt.c_str(), &tlcrypt);
|
||||
|
||||
if (e && (e[0] != '*')) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
throw no_supported_schemes();
|
||||
}
|
||||
|
||||
sstring hash_with_salt(const sstring& pass, const sstring& salt) {
|
||||
auto res = crypt_r(pass.c_str(), salt.c_str(), &tlcrypt);
|
||||
if (!res || (res[0] == '*')) {
|
||||
throw std::system_error(errno, std::system_category());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const char* prefix_for_scheme(scheme c) noexcept {
|
||||
switch (c) {
|
||||
case scheme::bcrypt_y: return "$2y$";
|
||||
case scheme::bcrypt_a: return "$2a$";
|
||||
case scheme::sha_512: return "$6$";
|
||||
case scheme::sha_256: return "$5$";
|
||||
case scheme::md5: return "$1$";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
no_supported_schemes::no_supported_schemes()
|
||||
: std::runtime_error("No allowed hashing schemes are supported on this system") {
|
||||
}
|
||||
|
||||
bool check(const sstring& pass, const sstring& salted_hash) {
|
||||
return detail::hash_with_salt(pass, salted_hash) == salted_hash;
|
||||
}
|
||||
|
||||
} // namespace auth::paswords
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <seastar/core/sstring.hh>
|
||||
|
||||
#include "seastarx.hh"
|
||||
|
||||
namespace auth::passwords {
|
||||
|
||||
class no_supported_schemes : public std::runtime_error {
|
||||
public:
|
||||
no_supported_schemes();
|
||||
};
|
||||
|
||||
///
|
||||
/// Apache Cassandra uses a library to provide the bcrypt scheme. Many Linux implementations do not support bcrypt, so
|
||||
/// we support alternatives. The cost is loss of direct compatibility with Apache Cassandra system tables.
|
||||
///
|
||||
enum class scheme {
|
||||
bcrypt_y,
|
||||
bcrypt_a,
|
||||
sha_512,
|
||||
sha_256,
|
||||
md5
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename RandomNumberEngine>
|
||||
sstring generate_random_salt_bytes(RandomNumberEngine& g) {
|
||||
static const sstring valid_bytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
|
||||
static constexpr std::size_t num_bytes = 16;
|
||||
std::uniform_int_distribution<std::size_t> dist(0, valid_bytes.size() - 1);
|
||||
sstring result(num_bytes, 0);
|
||||
|
||||
for (char& c : result) {
|
||||
c = valid_bytes[dist(g)];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
///
|
||||
/// Test each allowed hashing scheme and report the best supported one on the current system.
|
||||
///
|
||||
/// \throws \ref no_supported_schemes when none of the known schemes is supported.
|
||||
///
|
||||
scheme identify_best_supported_scheme();
|
||||
|
||||
const char* prefix_for_scheme(scheme) noexcept;
|
||||
|
||||
///
|
||||
/// Generate a implementation-specific salt string for hashing passwords.
|
||||
///
|
||||
/// The `RandomNumberEngine` is used to generate the string, which is an implementation-specific length.
|
||||
///
|
||||
/// \throws \ref no_supported_schemes when no known hashing schemes are supported on the system.
|
||||
///
|
||||
template <typename RandomNumberEngine>
|
||||
sstring generate_salt(RandomNumberEngine& g) {
|
||||
static const scheme scheme = identify_best_supported_scheme();
|
||||
static const sstring prefix = sstring(prefix_for_scheme(scheme));
|
||||
return prefix + generate_random_salt_bytes(g);
|
||||
}
|
||||
|
||||
///
|
||||
/// Hash a password combined with an implementation-specific salt string.
|
||||
///
|
||||
/// \throws \ref std::system_error when an unexpected implementation-specific error occurs.
|
||||
///
|
||||
sstring hash_with_salt(const sstring& pass, const sstring& salt);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
///
|
||||
/// Run a one-way hashing function on cleartext to produce encrypted text.
|
||||
///
|
||||
/// Prior to applying the hashing function, random salt is amended to the cleartext. The random salt bytes are generated
|
||||
/// according to the random number engine `g`.
|
||||
///
|
||||
/// The result is the encrypted cyphertext, and also the salt used but in a implementation-specific format.
|
||||
///
|
||||
/// \throws \ref std::system_error when the implementation-specific implementation fails to hash the cleartext.
|
||||
///
|
||||
template <typename RandomNumberEngine>
|
||||
sstring hash(const sstring& pass, RandomNumberEngine& g) {
|
||||
return detail::hash_with_salt(pass, detail::generate_salt(g));
|
||||
}
|
||||
|
||||
///
|
||||
/// Check that cleartext matches previously hashed cleartext with salt.
|
||||
///
|
||||
/// \ref salted_hash is the result of invoking \ref hash, which is the implementation-specific combination of the hashed
|
||||
/// password and the salt that was generated for it.
|
||||
///
|
||||
/// \returns `true` if the cleartext matches the salted hash.
|
||||
///
|
||||
/// \throws \ref std::system_error when an unexpected implementation-specific error occurs.
|
||||
///
|
||||
bool check(const sstring& pass, const sstring& salted_hash);
|
||||
|
||||
} // namespace auth::passwords
|
||||
@@ -24,9 +24,19 @@
|
||||
#include "auth/authorizer.hh"
|
||||
#include "auth/common.hh"
|
||||
#include "auth/service.hh"
|
||||
#include "db/config.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
permissions_cache_config permissions_cache_config::from_db_config(const db::config& dc) {
|
||||
permissions_cache_config c;
|
||||
c.max_entries = dc.permissions_cache_max_entries();
|
||||
c.validity_period = std::chrono::milliseconds(dc.permissions_validity_in_ms());
|
||||
c.update_period = std::chrono::milliseconds(dc.permissions_update_interval_in_ms());
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
permissions_cache::permissions_cache(const permissions_cache_config& c, service& ser, logging::logger& log)
|
||||
: _cache(c.max_entries, c.validity_period, c.update_period, log, [&ser, &log](const key_type& k) {
|
||||
log.debug("Refreshing permissions for {}", k.first);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "auth/resource.hh"
|
||||
#include "auth/role_or_anonymous.hh"
|
||||
#include "log.hh"
|
||||
#include "stdx.hh"
|
||||
#include "utils/hash.hh"
|
||||
#include "utils/loading_cache.hh"
|
||||
|
||||
@@ -58,6 +59,8 @@ namespace auth {
|
||||
class service;
|
||||
|
||||
struct permissions_cache_config final {
|
||||
static permissions_cache_config from_db_config(const db::config&);
|
||||
|
||||
std::size_t max_entries;
|
||||
std::chrono::milliseconds validity_period;
|
||||
std::chrono::milliseconds update_period;
|
||||
|
||||
@@ -61,7 +61,7 @@ std::ostream& operator<<(std::ostream& os, resource_kind kind) {
|
||||
return os;
|
||||
}
|
||||
|
||||
static const std::unordered_map<resource_kind, std::string_view> roots{
|
||||
static const std::unordered_map<resource_kind, stdx::string_view> roots{
|
||||
{resource_kind::data, "data"},
|
||||
{resource_kind::role, "roles"}};
|
||||
|
||||
@@ -101,25 +101,24 @@ static permission_set applicable_permissions(const role_resource_view& rv) {
|
||||
permission::DESCRIBE>();
|
||||
}
|
||||
|
||||
resource::resource(resource_kind kind) : _kind(kind) {
|
||||
_parts.emplace_back(roots.at(kind));
|
||||
resource::resource(resource_kind kind) : _kind(kind), _parts{sstring(roots.at(kind))} {
|
||||
}
|
||||
|
||||
resource::resource(resource_kind kind, utils::small_vector<sstring, 3> parts) : resource(kind) {
|
||||
resource::resource(resource_kind kind, std::vector<sstring> parts) : resource(kind) {
|
||||
_parts.reserve(parts.size() + 1);
|
||||
_parts.insert(_parts.end(), std::make_move_iterator(parts.begin()), std::make_move_iterator(parts.end()));
|
||||
}
|
||||
|
||||
resource::resource(data_resource_t, std::string_view keyspace) : resource(resource_kind::data) {
|
||||
_parts.emplace_back(keyspace);
|
||||
resource::resource(data_resource_t, stdx::string_view keyspace)
|
||||
: resource(resource_kind::data, std::vector<sstring>{sstring(keyspace)}) {
|
||||
}
|
||||
|
||||
resource::resource(data_resource_t, std::string_view keyspace, std::string_view table) : resource(resource_kind::data) {
|
||||
_parts.emplace_back(keyspace);
|
||||
_parts.emplace_back(table);
|
||||
resource::resource(data_resource_t, stdx::string_view keyspace, stdx::string_view table)
|
||||
: resource(resource_kind::data, std::vector<sstring>{sstring(keyspace), sstring(table)}) {
|
||||
}
|
||||
|
||||
resource::resource(role_resource_t, std::string_view role) : resource(resource_kind::role) {
|
||||
_parts.emplace_back(role);
|
||||
resource::resource(role_resource_t, stdx::string_view role)
|
||||
: resource(resource_kind::role, std::vector<sstring>{sstring(role)}) {
|
||||
}
|
||||
|
||||
sstring resource::name() const {
|
||||
@@ -174,7 +173,7 @@ data_resource_view::data_resource_view(const resource& r) : _resource(r) {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string_view> data_resource_view::keyspace() const {
|
||||
std::optional<stdx::string_view> data_resource_view::keyspace() const {
|
||||
if (_resource._parts.size() == 1) {
|
||||
return {};
|
||||
}
|
||||
@@ -182,7 +181,7 @@ std::optional<std::string_view> data_resource_view::keyspace() const {
|
||||
return _resource._parts[1];
|
||||
}
|
||||
|
||||
std::optional<std::string_view> data_resource_view::table() const {
|
||||
std::optional<stdx::string_view> data_resource_view::table() const {
|
||||
if (_resource._parts.size() <= 2) {
|
||||
return {};
|
||||
}
|
||||
@@ -211,7 +210,7 @@ role_resource_view::role_resource_view(const resource& r) : _resource(r) {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string_view> role_resource_view::role() const {
|
||||
std::optional<stdx::string_view> role_resource_view::role() const {
|
||||
if (_resource._parts.size() == 1) {
|
||||
return {};
|
||||
}
|
||||
@@ -231,9 +230,9 @@ std::ostream& operator<<(std::ostream& os, const role_resource_view& v) {
|
||||
return os;
|
||||
}
|
||||
|
||||
resource parse_resource(std::string_view name) {
|
||||
static const std::unordered_map<std::string_view, resource_kind> reverse_roots = [] {
|
||||
std::unordered_map<std::string_view, resource_kind> result;
|
||||
resource parse_resource(stdx::string_view name) {
|
||||
static const std::unordered_map<stdx::string_view, resource_kind> reverse_roots = [] {
|
||||
std::unordered_map<stdx::string_view, resource_kind> result;
|
||||
|
||||
for (const auto& pair : roots) {
|
||||
result.emplace(pair.second, pair.first);
|
||||
@@ -242,7 +241,7 @@ resource parse_resource(std::string_view name) {
|
||||
return result;
|
||||
}();
|
||||
|
||||
utils::small_vector<sstring, 3> parts;
|
||||
std::vector<sstring> parts;
|
||||
boost::split(parts, name, [](char ch) { return ch == '/'; });
|
||||
|
||||
if (parts.empty()) {
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
@@ -54,15 +54,15 @@
|
||||
|
||||
#include "auth/permission.hh"
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
#include "utils/hash.hh"
|
||||
#include "utils/small_vector.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
class invalid_resource_name : public std::invalid_argument {
|
||||
public:
|
||||
explicit invalid_resource_name(std::string_view name)
|
||||
: std::invalid_argument(format("The resource name '{}' is invalid.", name)) {
|
||||
explicit invalid_resource_name(stdx::string_view name)
|
||||
: std::invalid_argument(sprint("The resource name '%s' is invalid.", name)) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -98,16 +98,16 @@ struct role_resource_t final {};
|
||||
class resource final {
|
||||
resource_kind _kind;
|
||||
|
||||
utils::small_vector<sstring, 3> _parts;
|
||||
std::vector<sstring> _parts;
|
||||
|
||||
public:
|
||||
///
|
||||
/// A root resource of a particular kind.
|
||||
///
|
||||
explicit resource(resource_kind);
|
||||
resource(data_resource_t, std::string_view keyspace);
|
||||
resource(data_resource_t, std::string_view keyspace, std::string_view table);
|
||||
resource(role_resource_t, std::string_view role);
|
||||
resource(data_resource_t, stdx::string_view keyspace);
|
||||
resource(data_resource_t, stdx::string_view keyspace, stdx::string_view table);
|
||||
resource(role_resource_t, stdx::string_view role);
|
||||
|
||||
resource_kind kind() const noexcept {
|
||||
return _kind;
|
||||
@@ -123,7 +123,7 @@ public:
|
||||
permission_set applicable_permissions() const;
|
||||
|
||||
private:
|
||||
resource(resource_kind, utils::small_vector<sstring, 3> parts);
|
||||
resource(resource_kind, std::vector<sstring> parts);
|
||||
|
||||
friend class std::hash<resource>;
|
||||
friend class data_resource_view;
|
||||
@@ -131,7 +131,7 @@ private:
|
||||
|
||||
friend bool operator<(const resource&, const resource&);
|
||||
friend bool operator==(const resource&, const resource&);
|
||||
friend resource parse_resource(std::string_view);
|
||||
friend resource parse_resource(stdx::string_view);
|
||||
};
|
||||
|
||||
bool operator<(const resource&, const resource&);
|
||||
@@ -150,7 +150,7 @@ class resource_kind_mismatch : public std::invalid_argument {
|
||||
public:
|
||||
explicit resource_kind_mismatch(resource_kind expected, resource_kind actual)
|
||||
: std::invalid_argument(
|
||||
format("This resource has kind '{}', but was expected to have kind '{}'.", actual, expected)) {
|
||||
sprint("This resource has kind '%s', but was expected to have kind '%s'.", actual, expected)) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,9 +166,9 @@ public:
|
||||
///
|
||||
explicit data_resource_view(const resource& r);
|
||||
|
||||
std::optional<std::string_view> keyspace() const;
|
||||
std::optional<stdx::string_view> keyspace() const;
|
||||
|
||||
std::optional<std::string_view> table() const;
|
||||
std::optional<stdx::string_view> table() const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const data_resource_view&);
|
||||
@@ -187,7 +187,7 @@ public:
|
||||
///
|
||||
explicit role_resource_view(const resource&);
|
||||
|
||||
std::optional<std::string_view> role() const;
|
||||
std::optional<stdx::string_view> role() const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const role_resource_view&);
|
||||
@@ -197,20 +197,20 @@ std::ostream& operator<<(std::ostream&, const role_resource_view&);
|
||||
///
|
||||
/// \throws \ref invalid_resource_name when the name is malformed.
|
||||
///
|
||||
resource parse_resource(std::string_view name);
|
||||
resource parse_resource(stdx::string_view name);
|
||||
|
||||
const resource& root_data_resource();
|
||||
|
||||
inline resource make_data_resource(std::string_view keyspace) {
|
||||
inline resource make_data_resource(stdx::string_view keyspace) {
|
||||
return resource(data_resource_t{}, keyspace);
|
||||
}
|
||||
inline resource make_data_resource(std::string_view keyspace, std::string_view table) {
|
||||
inline resource make_data_resource(stdx::string_view keyspace, stdx::string_view table) {
|
||||
return resource(data_resource_t{}, keyspace, table);
|
||||
}
|
||||
|
||||
const resource& root_role_resource();
|
||||
|
||||
inline resource make_role_resource(std::string_view role) {
|
||||
inline resource make_role_resource(stdx::string_view role) {
|
||||
return resource(role_resource_t{}, role);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "auth/resource.hh"
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -59,31 +60,31 @@ public:
|
||||
|
||||
class role_already_exists : public roles_argument_exception {
|
||||
public:
|
||||
explicit role_already_exists(std::string_view role_name)
|
||||
: roles_argument_exception(format("Role {} already exists.", role_name)) {
|
||||
explicit role_already_exists(stdx::string_view role_name)
|
||||
: roles_argument_exception(sprint("Role %s already exists.", role_name)) {
|
||||
}
|
||||
};
|
||||
|
||||
class nonexistant_role : public roles_argument_exception {
|
||||
public:
|
||||
explicit nonexistant_role(std::string_view role_name)
|
||||
: roles_argument_exception(format("Role {} doesn't exist.", role_name)) {
|
||||
explicit nonexistant_role(stdx::string_view role_name)
|
||||
: roles_argument_exception(sprint("Role %s doesn't exist.", role_name)) {
|
||||
}
|
||||
};
|
||||
|
||||
class role_already_included : public roles_argument_exception {
|
||||
public:
|
||||
role_already_included(std::string_view grantee_name, std::string_view role_name)
|
||||
role_already_included(stdx::string_view grantee_name, stdx::string_view role_name)
|
||||
: roles_argument_exception(
|
||||
format("{} already includes role {}.", grantee_name, role_name)) {
|
||||
sprint("%s already includes role %s.", grantee_name, role_name)) {
|
||||
}
|
||||
};
|
||||
|
||||
class revoke_ungranted_role : public roles_argument_exception {
|
||||
public:
|
||||
revoke_ungranted_role(std::string_view revokee_name, std::string_view role_name)
|
||||
revoke_ungranted_role(stdx::string_view revokee_name, stdx::string_view role_name)
|
||||
: roles_argument_exception(
|
||||
format("{} was not granted role {}, so it cannot be revoked.", revokee_name, role_name)) {
|
||||
sprint("%s was not granted role %s, so it cannot be revoked.", revokee_name, role_name)) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,7 +104,7 @@ class role_manager {
|
||||
public:
|
||||
virtual ~role_manager() = default;
|
||||
|
||||
virtual std::string_view qualified_java_name() const noexcept = 0;
|
||||
virtual stdx::string_view qualified_java_name() const noexcept = 0;
|
||||
|
||||
virtual const resource_set& protected_resources() const = 0;
|
||||
|
||||
@@ -114,17 +115,17 @@ public:
|
||||
///
|
||||
/// \returns an exceptional future with \ref role_already_exists for a role that has previously been created.
|
||||
///
|
||||
virtual future<> create(std::string_view role_name, const role_config&) const = 0;
|
||||
virtual future<> create(stdx::string_view role_name, const role_config&) const = 0;
|
||||
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistant_role if the role does not exist.
|
||||
///
|
||||
virtual future<> drop(std::string_view role_name) const = 0;
|
||||
virtual future<> drop(stdx::string_view role_name) const = 0;
|
||||
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistant_role if the role does not exist.
|
||||
///
|
||||
virtual future<> alter(std::string_view role_name, const role_config_update&) const = 0;
|
||||
virtual future<> alter(stdx::string_view role_name, const role_config_update&) const = 0;
|
||||
|
||||
///
|
||||
/// Grant `role_name` to `grantee_name`.
|
||||
@@ -134,7 +135,7 @@ public:
|
||||
/// \returns an exceptional future with \ref role_already_included if granting the role would be redundant, or
|
||||
/// create a cycle.
|
||||
///
|
||||
virtual future<> grant(std::string_view grantee_name, std::string_view role_name) const = 0;
|
||||
virtual future<> grant(stdx::string_view grantee_name, stdx::string_view role_name) const = 0;
|
||||
|
||||
///
|
||||
/// Revoke `role_name` from `revokee_name`.
|
||||
@@ -143,26 +144,26 @@ public:
|
||||
///
|
||||
/// \returns an exceptional future with \ref revoke_ungranted_role if the role was not granted.
|
||||
///
|
||||
virtual future<> revoke(std::string_view revokee_name, std::string_view role_name) const = 0;
|
||||
virtual future<> revoke(stdx::string_view revokee_name, stdx::string_view role_name) const = 0;
|
||||
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistant_role if the role does not exist.
|
||||
///
|
||||
virtual future<role_set> query_granted(std::string_view grantee, recursive_role_query) const = 0;
|
||||
virtual future<role_set> query_granted(stdx::string_view grantee, recursive_role_query) const = 0;
|
||||
|
||||
virtual future<role_set> query_all() const = 0;
|
||||
|
||||
virtual future<bool> exists(std::string_view role_name) const = 0;
|
||||
virtual future<bool> exists(stdx::string_view role_name) const = 0;
|
||||
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistant_role if the role does not exist.
|
||||
///
|
||||
virtual future<bool> is_superuser(std::string_view role_name) const = 0;
|
||||
virtual future<bool> is_superuser(stdx::string_view role_name) const = 0;
|
||||
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistant_role if the role does not exist.
|
||||
///
|
||||
virtual future<bool> can_login(std::string_view role_name) const = 0;
|
||||
virtual future<bool> can_login(stdx::string_view role_name) const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <optional>
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <seastar/core/sstring.hh>
|
||||
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -37,7 +38,7 @@ public:
|
||||
std::optional<sstring> name{};
|
||||
|
||||
role_or_anonymous() = default;
|
||||
role_or_anonymous(std::string_view name) : name(name) {
|
||||
role_or_anonymous(stdx::string_view name) : name(name) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace meta {
|
||||
|
||||
namespace roles_table {
|
||||
|
||||
std::string_view creation_query() {
|
||||
stdx::string_view creation_query() {
|
||||
static const sstring instance = sprint(
|
||||
"CREATE TABLE %s ("
|
||||
" %s text PRIMARY KEY,"
|
||||
@@ -51,7 +51,7 @@ std::string_view creation_query() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::string_view qualified_name() noexcept {
|
||||
stdx::string_view qualified_name() noexcept {
|
||||
static const sstring instance = AUTH_KS + "." + sstring(name);
|
||||
return instance;
|
||||
}
|
||||
@@ -63,7 +63,8 @@ std::string_view qualified_name() noexcept {
|
||||
future<bool> default_role_row_satisfies(
|
||||
cql3::query_processor& qp,
|
||||
std::function<bool(const cql3::untyped_result_set_row&)> p) {
|
||||
static const sstring query = format("SELECT * FROM {} WHERE {} = ?",
|
||||
static const sstring query = sprint(
|
||||
"SELECT * FROM %s WHERE %s = ?",
|
||||
meta::roles_table::qualified_name(),
|
||||
meta::roles_table::role_col_name);
|
||||
|
||||
@@ -71,14 +72,12 @@ future<bool> default_role_row_satisfies(
|
||||
return qp.process(
|
||||
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.process(
|
||||
query,
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_timeout_config(),
|
||||
{meta::DEFAULT_SUPERUSER_NAME},
|
||||
true).then([&p](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
if (results->empty()) {
|
||||
@@ -97,13 +96,12 @@ future<bool> default_role_row_satisfies(
|
||||
future<bool> any_nondefault_role_row_satisfies(
|
||||
cql3::query_processor& qp,
|
||||
std::function<bool(const cql3::untyped_result_set_row&)> p) {
|
||||
static const sstring query = format("SELECT * FROM {}", meta::roles_table::qualified_name());
|
||||
static const sstring query = sprint("SELECT * FROM %s", meta::roles_table::qualified_name());
|
||||
|
||||
return do_with(std::move(p), [&qp](const auto& p) {
|
||||
return qp.process(
|
||||
query,
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_timeout_config()).then([&p](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
db::consistency_level::QUORUM).then([&p](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
if (results->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <functional>
|
||||
|
||||
#include <seastar/core/future.hh>
|
||||
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace cql3 {
|
||||
class query_processor;
|
||||
@@ -39,13 +40,13 @@ namespace meta {
|
||||
|
||||
namespace roles_table {
|
||||
|
||||
std::string_view creation_query();
|
||||
stdx::string_view creation_query();
|
||||
|
||||
constexpr std::string_view name{"roles", 5};
|
||||
constexpr stdx::string_view name{"roles", 5};
|
||||
|
||||
std::string_view qualified_name() noexcept;
|
||||
stdx::string_view qualified_name() noexcept;
|
||||
|
||||
constexpr std::string_view role_col_name{"role", 4};
|
||||
constexpr stdx::string_view role_col_name{"role", 4};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "auth/sasl_challenge.hh"
|
||||
|
||||
#include "exceptions/exceptions.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
/**
|
||||
* SASL PLAIN mechanism specifies that credentials are encoded in a
|
||||
* sequence of UTF-8 bytes, delimited by 0 (US-ASCII NUL).
|
||||
* The form is : {code}authzId<NUL>authnId<NUL>password<NUL>{code}
|
||||
* authzId is optional, and in fact we don't care about it here as we'll
|
||||
* set the authzId to match the authnId (that is, there is no concept of
|
||||
* a user being authorized to act on behalf of another).
|
||||
*
|
||||
* @param bytes encoded credentials string sent by the client
|
||||
* @return map containing the username/password pairs in the form an IAuthenticator
|
||||
* would expect
|
||||
* @throws javax.security.sasl.SaslException
|
||||
*/
|
||||
bytes plain_sasl_challenge::evaluate_response(bytes_view client_response) {
|
||||
sstring username, password;
|
||||
|
||||
auto b = client_response.crbegin();
|
||||
auto e = client_response.crend();
|
||||
auto i = b;
|
||||
|
||||
while (i != e) {
|
||||
if (*i == 0) {
|
||||
sstring tmp(i.base(), b.base());
|
||||
if (password.empty()) {
|
||||
password = std::move(tmp);
|
||||
} else if (username.empty()) {
|
||||
username = std::move(tmp);
|
||||
}
|
||||
b = ++i;
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if (username.empty()) {
|
||||
throw exceptions::authentication_exception("Authentication ID must not be null");
|
||||
}
|
||||
if (password.empty()) {
|
||||
throw exceptions::authentication_exception("Password must not be null");
|
||||
}
|
||||
|
||||
_username = std::move(username);
|
||||
_password = std::move(password);
|
||||
return {};
|
||||
}
|
||||
|
||||
bool plain_sasl_challenge::is_complete() const {
|
||||
return _username && _password;
|
||||
}
|
||||
|
||||
future<authenticated_user> plain_sasl_challenge::get_authenticated_user() const {
|
||||
return _when_complete(*_username, *_password);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 ScyllaDB
|
||||
*
|
||||
* Modified by ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include <seastar/core/future.hh>
|
||||
#include <seastar/core/sstring.hh>
|
||||
|
||||
#include "auth/authenticated_user.hh"
|
||||
#include "bytes.hh"
|
||||
#include "seastarx.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
///
|
||||
/// A stateful SASL challenge which supports many authentication schemes (depending on the implementation).
|
||||
///
|
||||
class sasl_challenge {
|
||||
public:
|
||||
virtual ~sasl_challenge() = default;
|
||||
|
||||
virtual bytes evaluate_response(bytes_view client_response) = 0;
|
||||
|
||||
virtual bool is_complete() const = 0;
|
||||
|
||||
virtual future<authenticated_user> get_authenticated_user() const = 0;
|
||||
};
|
||||
|
||||
class plain_sasl_challenge : public sasl_challenge {
|
||||
public:
|
||||
using completion_callback = std::function<future<authenticated_user>(std::string_view, std::string_view)>;
|
||||
|
||||
explicit plain_sasl_challenge(completion_callback f) : _when_complete(std::move(f)) {
|
||||
}
|
||||
|
||||
virtual bytes evaluate_response(bytes_view) override;
|
||||
|
||||
virtual bool is_complete() const override;
|
||||
|
||||
virtual future<authenticated_user> get_authenticated_user() const override;
|
||||
|
||||
private:
|
||||
std::optional<sstring> _username, _password;
|
||||
completion_callback _when_complete;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -36,12 +36,12 @@
|
||||
#include "auth/standard_role_manager.hh"
|
||||
#include "cql3/query_processor.hh"
|
||||
#include "cql3/untyped_result_set.hh"
|
||||
#include "db/consistency_level_type.hh"
|
||||
#include "db/config.hh"
|
||||
#include "db/consistency_level.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "log.hh"
|
||||
#include "service/migration_listener.hh"
|
||||
#include "utils/class_registrator.hh"
|
||||
#include "database.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -97,7 +97,7 @@ private:
|
||||
void on_drop_view(const sstring& ks_name, const sstring& view_name) override {}
|
||||
};
|
||||
|
||||
static future<> validate_role_exists(const service& ser, std::string_view role_name) {
|
||||
static future<> validate_role_exists(const service& ser, stdx::string_view role_name) {
|
||||
return ser.underlying_role_manager().exists(role_name).then([role_name](bool exists) {
|
||||
if (!exists) {
|
||||
throw nonexistant_role(role_name);
|
||||
@@ -105,6 +105,19 @@ static future<> validate_role_exists(const service& ser, std::string_view role_n
|
||||
});
|
||||
}
|
||||
|
||||
service_config service_config::from_db_config(const db::config& dc) {
|
||||
const qualified_name qualified_authorizer_name(meta::AUTH_PACKAGE_NAME, dc.authorizer());
|
||||
const qualified_name qualified_authenticator_name(meta::AUTH_PACKAGE_NAME, dc.authenticator());
|
||||
const qualified_name qualified_role_manager_name(meta::AUTH_PACKAGE_NAME, dc.role_manager());
|
||||
|
||||
service_config c;
|
||||
c.authorizer_java_name = qualified_authorizer_name;
|
||||
c.authenticator_java_name = qualified_authenticator_name;
|
||||
c.role_manager_java_name = qualified_role_manager_name;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
service::service(
|
||||
permissions_cache_config c,
|
||||
cql3::query_processor& qp,
|
||||
@@ -126,7 +139,8 @@ service::service(
|
||||
if ((_authenticator->qualified_java_name() == password_authenticator_name())
|
||||
&& (_role_manager->qualified_java_name() != standard_role_manager_name())) {
|
||||
throw incompatible_module_combination(
|
||||
format("The {} authenticator must be loaded alongside the {} role-manager.",
|
||||
sprint(
|
||||
"The %s authenticator must be loaded alongside the %s role-manager.",
|
||||
password_authenticator_name(),
|
||||
standard_role_manager_name()));
|
||||
}
|
||||
@@ -147,7 +161,7 @@ service::service(
|
||||
}
|
||||
|
||||
future<> service::create_keyspace_if_missing() const {
|
||||
auto& db = _qp.db();
|
||||
auto& db = _qp.db().local();
|
||||
|
||||
if (!db.has_keyspace(meta::AUTH_KS)) {
|
||||
std::map<sstring, sstring> opts{{"replication_factor", "1"}};
|
||||
@@ -170,9 +184,7 @@ future<> service::start() {
|
||||
return once_among_shards([this] {
|
||||
return create_keyspace_if_missing();
|
||||
}).then([this] {
|
||||
return _role_manager->start().then([this] {
|
||||
return when_all_succeed(_authorizer->start(), _authenticator->start());
|
||||
});
|
||||
return when_all_succeed(_role_manager->start(), _authorizer->start(), _authenticator->start());
|
||||
}).then([this] {
|
||||
_permissions_cache = std::make_unique<permissions_cache>(_permissions_cache_config, *this, log);
|
||||
}).then([this] {
|
||||
@@ -184,26 +196,24 @@ future<> service::start() {
|
||||
}
|
||||
|
||||
future<> service::stop() {
|
||||
// Only one of the shards has the listener registered, but let's try to
|
||||
// unregister on each one just to make sure.
|
||||
_migration_manager.unregister_listener(_migration_listener.get());
|
||||
|
||||
return _permissions_cache->stop().then([this] {
|
||||
return when_all_succeed(_role_manager->stop(), _authorizer->stop(), _authenticator->stop());
|
||||
});
|
||||
}
|
||||
|
||||
future<bool> service::has_existing_legacy_users() const {
|
||||
if (!_qp.db().has_schema(meta::AUTH_KS, meta::USERS_CF)) {
|
||||
if (!_qp.db().local().has_schema(meta::AUTH_KS, meta::USERS_CF)) {
|
||||
return make_ready_future<bool>(false);
|
||||
}
|
||||
|
||||
static const sstring default_user_query = format("SELECT * FROM {}.{} WHERE {} = ?",
|
||||
static const sstring default_user_query = sprint(
|
||||
"SELECT * FROM %s.%s WHERE %s = ?",
|
||||
meta::AUTH_KS,
|
||||
meta::USERS_CF,
|
||||
meta::user_name_col_name);
|
||||
|
||||
static const sstring all_users_query = format("SELECT * FROM {}.{} LIMIT 1",
|
||||
static const sstring all_users_query = sprint(
|
||||
"SELECT * FROM %s.%s LIMIT 1",
|
||||
meta::AUTH_KS,
|
||||
meta::USERS_CF);
|
||||
|
||||
@@ -213,7 +223,6 @@ future<bool> service::has_existing_legacy_users() const {
|
||||
return _qp.process(
|
||||
default_user_query,
|
||||
db::consistency_level::ONE,
|
||||
infinite_timeout_config,
|
||||
{meta::DEFAULT_SUPERUSER_NAME},
|
||||
true).then([this](auto results) {
|
||||
if (!results->empty()) {
|
||||
@@ -223,7 +232,6 @@ future<bool> service::has_existing_legacy_users() const {
|
||||
return _qp.process(
|
||||
default_user_query,
|
||||
db::consistency_level::QUORUM,
|
||||
infinite_timeout_config,
|
||||
{meta::DEFAULT_SUPERUSER_NAME},
|
||||
true).then([this](auto results) {
|
||||
if (!results->empty()) {
|
||||
@@ -232,8 +240,7 @@ future<bool> service::has_existing_legacy_users() const {
|
||||
|
||||
return _qp.process(
|
||||
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());
|
||||
});
|
||||
});
|
||||
@@ -246,7 +253,7 @@ service::get_uncached_permissions(const role_or_anonymous& maybe_role, const res
|
||||
return _authorizer->authorize(maybe_role, r);
|
||||
}
|
||||
|
||||
const std::string_view role_name = *maybe_role.name;
|
||||
const stdx::string_view role_name = *maybe_role.name;
|
||||
|
||||
return has_superuser(role_name).then([this, role_name, &r](bool superuser) {
|
||||
if (superuser) {
|
||||
@@ -260,7 +267,7 @@ service::get_uncached_permissions(const role_or_anonymous& maybe_role, const res
|
||||
return do_with(permission_set(), [this, role_name, &r](auto& all_perms) {
|
||||
return get_roles(role_name).then([this, &r, &all_perms](role_set all_roles) {
|
||||
return do_with(std::move(all_roles), [this, &r, &all_perms](const auto& all_roles) {
|
||||
return parallel_for_each(all_roles, [this, &r, &all_perms](std::string_view role_name) {
|
||||
return parallel_for_each(all_roles, [this, &r, &all_perms](stdx::string_view role_name) {
|
||||
return _authorizer->authorize(role_name, r).then([&all_perms](permission_set perms) {
|
||||
all_perms = permission_set::from_mask(all_perms.mask() | perms.mask());
|
||||
});
|
||||
@@ -277,7 +284,7 @@ future<permission_set> service::get_permissions(const role_or_anonymous& maybe_r
|
||||
return _permissions_cache->get(maybe_role, r);
|
||||
}
|
||||
|
||||
future<bool> service::has_superuser(std::string_view role_name) const {
|
||||
future<bool> service::has_superuser(stdx::string_view role_name) const {
|
||||
return this->get_roles(std::move(role_name)).then([this](role_set roles) {
|
||||
return do_with(std::move(roles), [this](const role_set& roles) {
|
||||
return do_with(false, roles.begin(), [this, &roles](bool& any_super, auto& iter) {
|
||||
@@ -295,7 +302,7 @@ future<bool> service::has_superuser(std::string_view role_name) const {
|
||||
});
|
||||
}
|
||||
|
||||
future<role_set> service::get_roles(std::string_view role_name) const {
|
||||
future<role_set> service::get_roles(stdx::string_view role_name) const {
|
||||
//
|
||||
// We may wish to cache this information in the future (as Apache Cassandra does).
|
||||
//
|
||||
@@ -306,7 +313,7 @@ future<role_set> service::get_roles(std::string_view role_name) const {
|
||||
future<bool> service::exists(const resource& r) const {
|
||||
switch (r.kind()) {
|
||||
case resource_kind::data: {
|
||||
const auto& db = _qp.db();
|
||||
const auto& db = _qp.db().local();
|
||||
|
||||
data_resource_view v(r);
|
||||
const auto keyspace = v.keyspace();
|
||||
@@ -401,7 +408,7 @@ static void validate_authentication_options_are_supported(
|
||||
|
||||
future<> create_role(
|
||||
const service& ser,
|
||||
std::string_view name,
|
||||
stdx::string_view name,
|
||||
const role_config& config,
|
||||
const authentication_options& options) {
|
||||
return ser.underlying_role_manager().create(name, config).then([&ser, name, &options] {
|
||||
@@ -425,7 +432,7 @@ future<> create_role(
|
||||
|
||||
future<> alter_role(
|
||||
const service& ser,
|
||||
std::string_view name,
|
||||
stdx::string_view name,
|
||||
const role_config_update& config_update,
|
||||
const authentication_options& options) {
|
||||
return ser.underlying_role_manager().alter(name, config_update).then([&ser, name, &options] {
|
||||
@@ -442,7 +449,7 @@ future<> alter_role(
|
||||
});
|
||||
}
|
||||
|
||||
future<> drop_role(const service& ser, std::string_view name) {
|
||||
future<> drop_role(const service& ser, stdx::string_view name) {
|
||||
return do_with(make_role_resource(name), [&ser, name](const resource& r) {
|
||||
auto& a = ser.underlying_authorizer();
|
||||
|
||||
@@ -458,14 +465,14 @@ future<> drop_role(const service& ser, std::string_view name) {
|
||||
});
|
||||
}
|
||||
|
||||
future<bool> has_role(const service& ser, std::string_view grantee, std::string_view name) {
|
||||
future<bool> has_role(const service& ser, stdx::string_view grantee, stdx::string_view name) {
|
||||
return when_all_succeed(
|
||||
validate_role_exists(ser, name),
|
||||
ser.get_roles(grantee)).then([name](role_set all_roles) {
|
||||
return make_ready_future<bool>(all_roles.count(sstring(name)) != 0);
|
||||
});
|
||||
}
|
||||
future<bool> has_role(const service& ser, const authenticated_user& u, std::string_view name) {
|
||||
future<bool> has_role(const service& ser, const authenticated_user& u, stdx::string_view name) {
|
||||
if (is_anonymous(u)) {
|
||||
return make_ready_future<bool>(false);
|
||||
}
|
||||
@@ -475,7 +482,7 @@ future<bool> has_role(const service& ser, const authenticated_user& u, std::stri
|
||||
|
||||
future<> grant_permissions(
|
||||
const service& ser,
|
||||
std::string_view role_name,
|
||||
stdx::string_view role_name,
|
||||
permission_set perms,
|
||||
const resource& r) {
|
||||
return validate_role_exists(ser, role_name).then([&ser, role_name, perms, &r] {
|
||||
@@ -483,7 +490,7 @@ future<> grant_permissions(
|
||||
});
|
||||
}
|
||||
|
||||
future<> grant_applicable_permissions(const service& ser, std::string_view role_name, const resource& r) {
|
||||
future<> grant_applicable_permissions(const service& ser, stdx::string_view role_name, const resource& r) {
|
||||
return grant_permissions(ser, role_name, r.applicable_permissions(), r);
|
||||
}
|
||||
future<> grant_applicable_permissions(const service& ser, const authenticated_user& u, const resource& r) {
|
||||
@@ -496,7 +503,7 @@ future<> grant_applicable_permissions(const service& ser, const authenticated_us
|
||||
|
||||
future<> revoke_permissions(
|
||||
const service& ser,
|
||||
std::string_view role_name,
|
||||
stdx::string_view role_name,
|
||||
permission_set perms,
|
||||
const resource& r) {
|
||||
return validate_role_exists(ser, role_name).then([&ser, role_name, perms, &r] {
|
||||
@@ -507,7 +514,7 @@ future<> revoke_permissions(
|
||||
future<std::vector<permission_details>> list_filtered_permissions(
|
||||
const service& ser,
|
||||
permission_set perms,
|
||||
std::optional<std::string_view> role_name,
|
||||
std::optional<stdx::string_view> role_name,
|
||||
const std::optional<std::pair<resource, recursive_permissions>>& resource_filter) {
|
||||
return ser.underlying_authorizer().list_all().then([&ser, perms, role_name, &resource_filter](
|
||||
std::vector<permission_details> all_details) {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
@@ -35,11 +35,16 @@
|
||||
#include "auth/permissions_cache.hh"
|
||||
#include "auth/role_manager.hh"
|
||||
#include "seastarx.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
namespace cql3 {
|
||||
class query_processor;
|
||||
}
|
||||
|
||||
namespace db {
|
||||
class config;
|
||||
}
|
||||
|
||||
namespace service {
|
||||
class migration_manager;
|
||||
class migration_listener;
|
||||
@@ -50,6 +55,8 @@ namespace auth {
|
||||
class role_or_anonymous;
|
||||
|
||||
struct service_config final {
|
||||
static service_config from_db_config(const db::config&);
|
||||
|
||||
sstring authorizer_java_name;
|
||||
sstring authenticator_java_name;
|
||||
sstring role_manager_java_name;
|
||||
@@ -134,13 +141,13 @@ public:
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistant_role if the role does not exist.
|
||||
///
|
||||
future<bool> has_superuser(std::string_view role_name) const;
|
||||
future<bool> has_superuser(stdx::string_view role_name) const;
|
||||
|
||||
///
|
||||
/// Return the set of all roles granted to the given role, including itself and roles granted through other roles.
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistent_role if the role does not exist.
|
||||
future<role_set> get_roles(std::string_view role_name) const;
|
||||
future<role_set> get_roles(stdx::string_view role_name) const;
|
||||
|
||||
future<bool> exists(const resource&) const;
|
||||
|
||||
@@ -190,7 +197,7 @@ bool is_protected(const service&, const resource&) noexcept;
|
||||
///
|
||||
future<> create_role(
|
||||
const service&,
|
||||
std::string_view name,
|
||||
stdx::string_view name,
|
||||
const role_config&,
|
||||
const authentication_options&);
|
||||
|
||||
@@ -203,7 +210,7 @@ future<> create_role(
|
||||
///
|
||||
future<> alter_role(
|
||||
const service&,
|
||||
std::string_view name,
|
||||
stdx::string_view name,
|
||||
const role_config_update&,
|
||||
const authentication_options&);
|
||||
|
||||
@@ -212,20 +219,20 @@ future<> alter_role(
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistant_role if the named role does not exist.
|
||||
///
|
||||
future<> drop_role(const service&, std::string_view name);
|
||||
future<> drop_role(const service&, stdx::string_view name);
|
||||
|
||||
///
|
||||
/// Check if `grantee` has been granted the named role.
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistent_role if `grantee` or `name` do not exist.
|
||||
///
|
||||
future<bool> has_role(const service&, std::string_view grantee, std::string_view name);
|
||||
future<bool> has_role(const service&, stdx::string_view grantee, stdx::string_view name);
|
||||
///
|
||||
/// Check if the authenticated user has been granted the named role.
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistent_role if the user or `name` do not exist.
|
||||
///
|
||||
future<bool> has_role(const service&, const authenticated_user&, std::string_view name);
|
||||
future<bool> has_role(const service&, const authenticated_user&, stdx::string_view name);
|
||||
|
||||
///
|
||||
/// \returns an exceptional future with \ref nonexistent_role if the named role does not exist.
|
||||
@@ -235,7 +242,7 @@ future<bool> has_role(const service&, const authenticated_user&, std::string_vie
|
||||
///
|
||||
future<> grant_permissions(
|
||||
const service&,
|
||||
std::string_view role_name,
|
||||
stdx::string_view role_name,
|
||||
permission_set,
|
||||
const resource&);
|
||||
|
||||
@@ -247,7 +254,7 @@ future<> grant_permissions(
|
||||
/// \returns an exceptional future with \ref unsupported_authorization_operation if granting permissions is not
|
||||
/// supported.
|
||||
///
|
||||
future<> grant_applicable_permissions(const service&, std::string_view role_name, const resource&);
|
||||
future<> grant_applicable_permissions(const service&, stdx::string_view role_name, const resource&);
|
||||
future<> grant_applicable_permissions(const service&, const authenticated_user&, const resource&);
|
||||
|
||||
///
|
||||
@@ -258,7 +265,7 @@ future<> grant_applicable_permissions(const service&, const authenticated_user&,
|
||||
///
|
||||
future<> revoke_permissions(
|
||||
const service&,
|
||||
std::string_view role_name,
|
||||
stdx::string_view role_name,
|
||||
permission_set,
|
||||
const resource&);
|
||||
|
||||
@@ -283,7 +290,7 @@ using recursive_permissions = bool_class<struct recursive_permissions_tag>;
|
||||
future<std::vector<permission_details>> list_filtered_permissions(
|
||||
const service&,
|
||||
permission_set,
|
||||
std::optional<std::string_view> role_name,
|
||||
std::optional<stdx::string_view> role_name,
|
||||
const std::optional<std::pair<resource, recursive_permissions>>& resource_filter);
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "auth/standard_role_manager.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <experimental/optional>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "log.hh"
|
||||
#include "utils/class_registrator.hh"
|
||||
#include "database.hh"
|
||||
|
||||
namespace auth {
|
||||
|
||||
@@ -47,9 +46,9 @@ namespace meta {
|
||||
|
||||
namespace role_members_table {
|
||||
|
||||
constexpr std::string_view name{"role_members" , 12};
|
||||
constexpr stdx::string_view name{"role_members" , 12};
|
||||
|
||||
static std::string_view qualified_name() noexcept {
|
||||
static stdx::string_view qualified_name() noexcept {
|
||||
static const sstring instance = AUTH_KS + "." + sstring(name);
|
||||
return instance;
|
||||
}
|
||||
@@ -73,7 +72,7 @@ struct record final {
|
||||
role_set member_of;
|
||||
};
|
||||
|
||||
static db::consistency_level consistency_for_role(std::string_view role_name) noexcept {
|
||||
static db::consistency_level consistency_for_role(stdx::string_view role_name) noexcept {
|
||||
if (role_name == meta::DEFAULT_SUPERUSER_NAME) {
|
||||
return db::consistency_level::QUORUM;
|
||||
}
|
||||
@@ -81,24 +80,24 @@ static db::consistency_level consistency_for_role(std::string_view role_name) no
|
||||
return db::consistency_level::LOCAL_ONE;
|
||||
}
|
||||
|
||||
static future<std::optional<record>> find_record(cql3::query_processor& qp, std::string_view role_name) {
|
||||
static const sstring query = format("SELECT * FROM {} WHERE {} = ?",
|
||||
static future<stdx::optional<record>> find_record(cql3::query_processor& qp, stdx::string_view role_name) {
|
||||
static const sstring query = sprint(
|
||||
"SELECT * FROM %s WHERE %s = ?",
|
||||
meta::roles_table::qualified_name(),
|
||||
meta::roles_table::role_col_name);
|
||||
|
||||
return qp.process(
|
||||
query,
|
||||
consistency_for_role(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name)},
|
||||
true).then([](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
if (results->empty()) {
|
||||
return std::optional<record>();
|
||||
return stdx::optional<record>();
|
||||
}
|
||||
|
||||
const cql3::untyped_result_set_row& row = results->one();
|
||||
|
||||
return std::make_optional(
|
||||
return stdx::make_optional(
|
||||
record{
|
||||
row.get_as<sstring>(sstring(meta::roles_table::role_col_name)),
|
||||
row.get_as<bool>("is_superuser"),
|
||||
@@ -109,8 +108,8 @@ static future<std::optional<record>> find_record(cql3::query_processor& qp, std:
|
||||
});
|
||||
}
|
||||
|
||||
static future<record> require_record(cql3::query_processor& qp, std::string_view role_name) {
|
||||
return find_record(qp, role_name).then([role_name](std::optional<record> mr) {
|
||||
static future<record> require_record(cql3::query_processor& qp, stdx::string_view role_name) {
|
||||
return find_record(qp, role_name).then([role_name](stdx::optional<record> mr) {
|
||||
if (!mr) {
|
||||
throw nonexistant_role(role_name);
|
||||
}
|
||||
@@ -123,12 +122,12 @@ static bool has_can_login(const cql3::untyped_result_set_row& row) {
|
||||
return row.has("can_login") && !(boolean_type->deserialize(row.get_blob("can_login")).is_null());
|
||||
}
|
||||
|
||||
std::string_view standard_role_manager_name() noexcept {
|
||||
stdx::string_view standard_role_manager_name() noexcept {
|
||||
static const sstring instance = meta::AUTH_PACKAGE_NAME + "CassandraRoleManager";
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::string_view standard_role_manager::qualified_java_name() const noexcept {
|
||||
stdx::string_view standard_role_manager::qualified_java_name() const noexcept {
|
||||
return standard_role_manager_name();
|
||||
}
|
||||
|
||||
@@ -166,14 +165,14 @@ future<> standard_role_manager::create_metadata_tables_if_missing() const {
|
||||
future<> standard_role_manager::create_default_role_if_missing() const {
|
||||
return default_role_row_satisfies(_qp, &has_can_login).then([this](bool exists) {
|
||||
if (!exists) {
|
||||
static const sstring query = format("INSERT INTO {} ({}, is_superuser, can_login) VALUES (?, true, true)",
|
||||
static const sstring query = sprint(
|
||||
"INSERT INTO %s (%s, is_superuser, can_login) VALUES (?, true, true)",
|
||||
meta::roles_table::qualified_name(),
|
||||
meta::roles_table::role_col_name);
|
||||
|
||||
return _qp.process(
|
||||
query,
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_timeout_config(),
|
||||
{meta::DEFAULT_SUPERUSER_NAME}).then([](auto&&) {
|
||||
log.info("Created default superuser role '{}'.", meta::DEFAULT_SUPERUSER_NAME);
|
||||
return make_ready_future<>();
|
||||
@@ -190,17 +189,16 @@ future<> standard_role_manager::create_default_role_if_missing() const {
|
||||
static const sstring legacy_table_name{"users"};
|
||||
|
||||
bool standard_role_manager::legacy_metadata_exists() const {
|
||||
return _qp.db().has_schema(meta::AUTH_KS, legacy_table_name);
|
||||
return _qp.db().local().has_schema(meta::AUTH_KS, legacy_table_name);
|
||||
}
|
||||
|
||||
future<> standard_role_manager::migrate_legacy_metadata() const {
|
||||
log.info("Starting migration of legacy user metadata.");
|
||||
static const sstring query = format("SELECT * FROM {}.{}", meta::AUTH_KS, legacy_table_name);
|
||||
static const sstring query = sprint("SELECT * FROM %s.%s", meta::AUTH_KS, legacy_table_name);
|
||||
|
||||
return _qp.process(
|
||||
query,
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_timeout_config()).then([this](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
db::consistency_level::QUORUM).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_as<bool>("super");
|
||||
@@ -226,7 +224,7 @@ future<> standard_role_manager::start() {
|
||||
return this->create_metadata_tables_if_missing().then([this] {
|
||||
_stopped = auth::do_after_system_ready(_as, [this] {
|
||||
return seastar::async([this] {
|
||||
wait_for_schema_agreement(_migration_manager, _qp.db(), _as).get0();
|
||||
wait_for_schema_agreement(_migration_manager, _qp.db().local()).get0();
|
||||
|
||||
if (any_nondefault_role_row_satisfies(_qp, &has_can_login).get0()) {
|
||||
if (this->legacy_metadata_exists()) {
|
||||
@@ -250,24 +248,24 @@ future<> standard_role_manager::start() {
|
||||
|
||||
future<> standard_role_manager::stop() {
|
||||
_as.request_abort();
|
||||
return _stopped.handle_exception_type([] (const sleep_aborted&) { }).handle_exception_type([](const abort_requested_exception&) {});;
|
||||
return _stopped.handle_exception_type([] (const sleep_aborted&) { });
|
||||
}
|
||||
|
||||
future<> standard_role_manager::create_or_replace(std::string_view role_name, const role_config& c) const {
|
||||
static const sstring query = format("INSERT INTO {} ({}, is_superuser, can_login) VALUES (?, ?, ?)",
|
||||
future<> standard_role_manager::create_or_replace(stdx::string_view role_name, const role_config& c) const {
|
||||
static const sstring query = sprint(
|
||||
"INSERT INTO %s (%s, is_superuser, can_login) VALUES (?, ?, ?)",
|
||||
meta::roles_table::qualified_name(),
|
||||
meta::roles_table::role_col_name);
|
||||
|
||||
return _qp.process(
|
||||
query,
|
||||
consistency_for_role(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name), c.is_superuser, c.can_login},
|
||||
true).discard_result();
|
||||
}
|
||||
|
||||
future<>
|
||||
standard_role_manager::create(std::string_view role_name, const role_config& c) const {
|
||||
standard_role_manager::create(stdx::string_view role_name, const role_config& c) const {
|
||||
return this->exists(role_name).then([this, role_name, &c](bool role_exists) {
|
||||
if (role_exists) {
|
||||
throw role_already_exists(role_name);
|
||||
@@ -278,7 +276,7 @@ standard_role_manager::create(std::string_view role_name, const role_config& c)
|
||||
}
|
||||
|
||||
future<>
|
||||
standard_role_manager::alter(std::string_view role_name, const role_config_update& u) const {
|
||||
standard_role_manager::alter(stdx::string_view role_name, const role_config_update& u) const {
|
||||
static const auto build_column_assignments = [](const role_config_update& u) -> sstring {
|
||||
std::vector<sstring> assignments;
|
||||
|
||||
@@ -299,17 +297,17 @@ standard_role_manager::alter(std::string_view role_name, const role_config_updat
|
||||
}
|
||||
|
||||
return _qp.process(
|
||||
format("UPDATE {} SET {} WHERE {} = ?",
|
||||
sprint(
|
||||
"UPDATE %s SET %s WHERE %s = ?",
|
||||
meta::roles_table::qualified_name(),
|
||||
build_column_assignments(u),
|
||||
meta::roles_table::role_col_name),
|
||||
consistency_for_role(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name)}).discard_result();
|
||||
});
|
||||
}
|
||||
|
||||
future<> standard_role_manager::drop(std::string_view role_name) const {
|
||||
future<> standard_role_manager::drop(stdx::string_view role_name) const {
|
||||
return this->exists(role_name).then([this, role_name](bool role_exists) {
|
||||
if (!role_exists) {
|
||||
throw nonexistant_role(role_name);
|
||||
@@ -317,13 +315,13 @@ future<> standard_role_manager::drop(std::string_view role_name) const {
|
||||
|
||||
// First, revoke this role from all roles that are members of it.
|
||||
const auto revoke_from_members = [this, role_name] {
|
||||
static const sstring query = format("SELECT member FROM {} WHERE role = ?",
|
||||
static const sstring query = sprint(
|
||||
"SELECT member FROM %s WHERE role = ?",
|
||||
meta::role_members_table::qualified_name());
|
||||
|
||||
return _qp.process(
|
||||
query,
|
||||
consistency_for_role(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name)}).then([this, role_name](::shared_ptr<cql3::untyped_result_set> members) {
|
||||
return parallel_for_each(
|
||||
members->begin(),
|
||||
@@ -355,14 +353,14 @@ future<> standard_role_manager::drop(std::string_view role_name) const {
|
||||
|
||||
// Finally, delete the role itself.
|
||||
auto delete_role = [this, role_name] {
|
||||
static const sstring query = format("DELETE FROM {} WHERE {} = ?",
|
||||
static const sstring query = sprint(
|
||||
"DELETE FROM %s WHERE %s = ?",
|
||||
meta::roles_table::qualified_name(),
|
||||
meta::roles_table::role_col_name);
|
||||
|
||||
return _qp.process(
|
||||
query,
|
||||
consistency_for_role(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name)}).discard_result();
|
||||
};
|
||||
|
||||
@@ -374,14 +372,14 @@ future<> standard_role_manager::drop(std::string_view role_name) const {
|
||||
|
||||
future<>
|
||||
standard_role_manager::modify_membership(
|
||||
std::string_view grantee_name,
|
||||
std::string_view role_name,
|
||||
stdx::string_view grantee_name,
|
||||
stdx::string_view role_name,
|
||||
membership_change ch) const {
|
||||
|
||||
|
||||
const auto modify_roles = [this, role_name, grantee_name, ch] {
|
||||
const auto query = format(
|
||||
"UPDATE {} SET member_of = member_of {} ? WHERE {} = ?",
|
||||
const auto query = sprint(
|
||||
"UPDATE %s SET member_of = member_of %s ? WHERE %s = ?",
|
||||
meta::roles_table::qualified_name(),
|
||||
(ch == membership_change::add ? '+' : '-'),
|
||||
meta::roles_table::role_col_name);
|
||||
@@ -389,7 +387,6 @@ standard_role_manager::modify_membership(
|
||||
return _qp.process(
|
||||
query,
|
||||
consistency_for_role(grantee_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{role_set{sstring(role_name)}, sstring(grantee_name)}).discard_result();
|
||||
};
|
||||
|
||||
@@ -397,18 +394,18 @@ standard_role_manager::modify_membership(
|
||||
switch (ch) {
|
||||
case membership_change::add:
|
||||
return _qp.process(
|
||||
format("INSERT INTO {} (role, member) VALUES (?, ?)",
|
||||
sprint(
|
||||
"INSERT INTO %s (role, member) VALUES (?, ?)",
|
||||
meta::role_members_table::qualified_name()),
|
||||
consistency_for_role(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name), sstring(grantee_name)}).discard_result();
|
||||
|
||||
case membership_change::remove:
|
||||
return _qp.process(
|
||||
format("DELETE FROM {} WHERE role = ? AND member = ?",
|
||||
sprint(
|
||||
"DELETE FROM %s WHERE role = ? AND member = ?",
|
||||
meta::role_members_table::qualified_name()),
|
||||
consistency_for_role(role_name),
|
||||
internal_distributed_timeout_config(),
|
||||
{sstring(role_name), sstring(grantee_name)}).discard_result();
|
||||
}
|
||||
|
||||
@@ -419,7 +416,7 @@ standard_role_manager::modify_membership(
|
||||
}
|
||||
|
||||
future<>
|
||||
standard_role_manager::grant(std::string_view grantee_name, std::string_view role_name) const {
|
||||
standard_role_manager::grant(stdx::string_view grantee_name, stdx::string_view role_name) const {
|
||||
const auto check_redundant = [this, role_name, grantee_name] {
|
||||
return this->query_granted(
|
||||
grantee_name,
|
||||
@@ -450,7 +447,7 @@ standard_role_manager::grant(std::string_view grantee_name, std::string_view rol
|
||||
}
|
||||
|
||||
future<>
|
||||
standard_role_manager::revoke(std::string_view revokee_name, std::string_view role_name) const {
|
||||
standard_role_manager::revoke(stdx::string_view revokee_name, stdx::string_view role_name) const {
|
||||
return this->exists(role_name).then([this, revokee_name, role_name](bool role_exists) {
|
||||
if (!role_exists) {
|
||||
throw nonexistant_role(sstring(role_name));
|
||||
@@ -472,7 +469,7 @@ standard_role_manager::revoke(std::string_view revokee_name, std::string_view ro
|
||||
|
||||
static future<> collect_roles(
|
||||
cql3::query_processor& qp,
|
||||
std::string_view grantee_name,
|
||||
stdx::string_view grantee_name,
|
||||
bool recurse,
|
||||
role_set& roles) {
|
||||
return require_record(qp, grantee_name).then([&qp, &roles, recurse](record r) {
|
||||
@@ -490,7 +487,7 @@ static future<> collect_roles(
|
||||
});
|
||||
}
|
||||
|
||||
future<role_set> standard_role_manager::query_granted(std::string_view grantee_name, recursive_role_query m) const {
|
||||
future<role_set> standard_role_manager::query_granted(stdx::string_view grantee_name, recursive_role_query m) const {
|
||||
const bool recurse = (m == recursive_role_query::yes);
|
||||
|
||||
return do_with(
|
||||
@@ -501,17 +498,15 @@ future<role_set> standard_role_manager::query_granted(std::string_view grantee_n
|
||||
}
|
||||
|
||||
future<role_set> standard_role_manager::query_all() const {
|
||||
static const sstring query = format("SELECT {} FROM {}",
|
||||
static const sstring query = sprint(
|
||||
"SELECT %s FROM %s",
|
||||
meta::roles_table::role_col_name,
|
||||
meta::roles_table::qualified_name());
|
||||
|
||||
// To avoid many copies of a view.
|
||||
static const auto role_col_name_string = sstring(meta::roles_table::role_col_name);
|
||||
|
||||
return _qp.process(
|
||||
query,
|
||||
db::consistency_level::QUORUM,
|
||||
internal_distributed_timeout_config()).then([](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
return _qp.process(query, db::consistency_level::QUORUM).then([](::shared_ptr<cql3::untyped_result_set> results) {
|
||||
role_set roles;
|
||||
|
||||
std::transform(
|
||||
@@ -526,19 +521,19 @@ future<role_set> standard_role_manager::query_all() const {
|
||||
});
|
||||
}
|
||||
|
||||
future<bool> standard_role_manager::exists(std::string_view role_name) const {
|
||||
return find_record(_qp, role_name).then([](std::optional<record> mr) {
|
||||
future<bool> standard_role_manager::exists(stdx::string_view role_name) const {
|
||||
return find_record(_qp, role_name).then([](stdx::optional<record> mr) {
|
||||
return static_cast<bool>(mr);
|
||||
});
|
||||
}
|
||||
|
||||
future<bool> standard_role_manager::is_superuser(std::string_view role_name) const {
|
||||
future<bool> standard_role_manager::is_superuser(stdx::string_view role_name) const {
|
||||
return require_record(_qp, role_name).then([](record r) {
|
||||
return r.is_superuser;
|
||||
});
|
||||
}
|
||||
|
||||
future<bool> standard_role_manager::can_login(std::string_view role_name) const {
|
||||
future<bool> standard_role_manager::can_login(stdx::string_view role_name) const {
|
||||
return require_record(_qp, role_name).then([](record r) {
|
||||
return r.can_login;
|
||||
});
|
||||
|
||||
@@ -23,13 +23,14 @@
|
||||
|
||||
#include "auth/role_manager.hh"
|
||||
|
||||
#include <string_view>
|
||||
#include <experimental/string_view>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <seastar/core/abort_source.hh>
|
||||
#include <seastar/core/future.hh>
|
||||
#include <seastar/core/sstring.hh>
|
||||
|
||||
#include "stdx.hh"
|
||||
#include "seastarx.hh"
|
||||
|
||||
namespace cql3 {
|
||||
@@ -42,7 +43,7 @@ class migration_manager;
|
||||
|
||||
namespace auth {
|
||||
|
||||
std::string_view standard_role_manager_name() noexcept;
|
||||
stdx::string_view standard_role_manager_name() noexcept;
|
||||
|
||||
class standard_role_manager final : public role_manager {
|
||||
cql3::query_processor& _qp;
|
||||
@@ -57,7 +58,7 @@ public:
|
||||
, _stopped(make_ready_future<>()) {
|
||||
}
|
||||
|
||||
virtual std::string_view qualified_java_name() const noexcept override;
|
||||
virtual stdx::string_view qualified_java_name() const noexcept override;
|
||||
|
||||
virtual const resource_set& protected_resources() const override;
|
||||
|
||||
@@ -65,25 +66,25 @@ public:
|
||||
|
||||
virtual future<> stop() override;
|
||||
|
||||
virtual future<> create(std::string_view role_name, const role_config&) const override;
|
||||
virtual future<> create(stdx::string_view role_name, const role_config&) const override;
|
||||
|
||||
virtual future<> drop(std::string_view role_name) const override;
|
||||
virtual future<> drop(stdx::string_view role_name) const override;
|
||||
|
||||
virtual future<> alter(std::string_view role_name, const role_config_update&) const override;
|
||||
virtual future<> alter(stdx::string_view role_name, const role_config_update&) const override;
|
||||
|
||||
virtual future<> grant(std::string_view grantee_name, std::string_view role_name) const override;
|
||||
virtual future<> grant(stdx::string_view grantee_name, stdx::string_view role_name) const override;
|
||||
|
||||
virtual future<> revoke(std::string_view revokee_name, std::string_view role_name) const override;
|
||||
virtual future<> revoke(stdx::string_view revokee_name, stdx::string_view role_name) const override;
|
||||
|
||||
virtual future<role_set> query_granted(std::string_view grantee_name, recursive_role_query) const override;
|
||||
virtual future<role_set> query_granted(stdx::string_view grantee_name, recursive_role_query) const override;
|
||||
|
||||
virtual future<role_set> query_all() const override;
|
||||
|
||||
virtual future<bool> exists(std::string_view role_name) const override;
|
||||
virtual future<bool> exists(stdx::string_view role_name) const override;
|
||||
|
||||
virtual future<bool> is_superuser(std::string_view role_name) const override;
|
||||
virtual future<bool> is_superuser(stdx::string_view role_name) const override;
|
||||
|
||||
virtual future<bool> can_login(std::string_view role_name) const override;
|
||||
virtual future<bool> can_login(stdx::string_view role_name) const override;
|
||||
|
||||
private:
|
||||
enum class membership_change { add, remove };
|
||||
@@ -96,9 +97,9 @@ private:
|
||||
|
||||
future<> create_default_role_if_missing() const;
|
||||
|
||||
future<> create_or_replace(std::string_view role_name, const role_config&) const;
|
||||
future<> create_or_replace(stdx::string_view role_name, const role_config&) const;
|
||||
|
||||
future<> modify_membership(std::string_view role_name, std::string_view grantee_name, membership_change) const;
|
||||
future<> modify_membership(stdx::string_view role_name, stdx::string_view grantee_name, membership_change) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "auth/default_authorizer.hh"
|
||||
#include "auth/password_authenticator.hh"
|
||||
#include "auth/permission.hh"
|
||||
#include "db/config.hh"
|
||||
#include "utils/class_registrator.hh"
|
||||
|
||||
namespace auth {
|
||||
@@ -117,19 +118,19 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
virtual future<> create(std::string_view role_name, const authentication_options& options) const override {
|
||||
virtual future<> create(stdx::string_view role_name, const authentication_options& options) const override {
|
||||
return _authenticator->create(role_name, options);
|
||||
}
|
||||
|
||||
virtual future<> alter(std::string_view role_name, const authentication_options& options) const override {
|
||||
virtual future<> alter(stdx::string_view role_name, const authentication_options& options) const override {
|
||||
return _authenticator->alter(role_name, options);
|
||||
}
|
||||
|
||||
virtual future<> drop(std::string_view role_name) const override {
|
||||
virtual future<> drop(stdx::string_view role_name) const override {
|
||||
return _authenticator->drop(role_name);
|
||||
}
|
||||
|
||||
virtual future<custom_options> query_custom_options(std::string_view role_name) const override {
|
||||
virtual future<custom_options> query_custom_options(stdx::string_view role_name) const override {
|
||||
return _authenticator->query_custom_options(role_name);
|
||||
}
|
||||
|
||||
@@ -217,11 +218,11 @@ public:
|
||||
return make_ready_future<permission_set>(transitional_permissions);
|
||||
}
|
||||
|
||||
virtual future<> grant(std::string_view s, permission_set ps, const resource& r) const override {
|
||||
virtual future<> grant(stdx::string_view s, permission_set ps, const resource& r) const override {
|
||||
return _authorizer->grant(s, std::move(ps), r);
|
||||
}
|
||||
|
||||
virtual future<> revoke(std::string_view s, permission_set ps, const resource& r) const override {
|
||||
virtual future<> revoke(stdx::string_view s, permission_set ps, const resource& r) const override {
|
||||
return _authorizer->revoke(s, std::move(ps), r);
|
||||
}
|
||||
|
||||
@@ -229,7 +230,7 @@ public:
|
||||
return _authorizer->list_all();
|
||||
}
|
||||
|
||||
virtual future<> revoke_all(std::string_view s) const override {
|
||||
virtual future<> revoke_all(stdx::string_view s) const override {
|
||||
return _authorizer->revoke_all(s);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ protected:
|
||||
, _io_priority(iop)
|
||||
, _interval(interval)
|
||||
, _update_timer([this] { adjust(); })
|
||||
, _control_points()
|
||||
, _control_points({{0,0}})
|
||||
, _current_backlog(std::move(backlog))
|
||||
, _inflight_update(make_ready_future<>())
|
||||
{
|
||||
@@ -96,12 +96,6 @@ protected:
|
||||
}
|
||||
|
||||
virtual ~backlog_controller() {}
|
||||
public:
|
||||
backlog_controller(backlog_controller&&) = default;
|
||||
float backlog_of_shares(float shares) const;
|
||||
seastar::scheduling_group sg() {
|
||||
return _scheduling_group;
|
||||
}
|
||||
};
|
||||
|
||||
// memtable flush CPU controller.
|
||||
@@ -125,7 +119,7 @@ public:
|
||||
flush_controller(seastar::scheduling_group sg, const ::io_priority_class& iop, float static_shares) : backlog_controller(sg, iop, static_shares) {}
|
||||
flush_controller(seastar::scheduling_group sg, const ::io_priority_class& iop, std::chrono::milliseconds interval, float soft_limit, std::function<float()> current_dirty)
|
||||
: backlog_controller(sg, iop, std::move(interval),
|
||||
std::vector<backlog_controller::control_point>({{0.0, 0.0}, {soft_limit, 10}, {soft_limit + (hard_dirty_limit - soft_limit) / 2, 200} , {hard_dirty_limit, 1000}}),
|
||||
std::vector<backlog_controller::control_point>({{soft_limit, 100}, {soft_limit + (hard_dirty_limit - soft_limit) / 2, 200} , {hard_dirty_limit, 1000}}),
|
||||
std::move(current_dirty)
|
||||
)
|
||||
{}
|
||||
@@ -134,12 +128,10 @@ public:
|
||||
class compaction_controller : public backlog_controller {
|
||||
public:
|
||||
static constexpr unsigned normalization_factor = 30;
|
||||
static constexpr float disable_backlog = std::numeric_limits<double>::infinity();
|
||||
static constexpr float backlog_disabled(float backlog) { return std::isinf(backlog); }
|
||||
compaction_controller(seastar::scheduling_group sg, const ::io_priority_class& iop, float static_shares) : backlog_controller(sg, iop, static_shares) {}
|
||||
compaction_controller(seastar::scheduling_group sg, const ::io_priority_class& iop, std::chrono::milliseconds interval, std::function<float()> current_backlog)
|
||||
: backlog_controller(sg, iop, std::move(interval),
|
||||
std::vector<backlog_controller::control_point>({{0.0, 50}, {1.5, 100} , {normalization_factor, 1000}}),
|
||||
std::vector<backlog_controller::control_point>({{0.5, 10}, {1.5, 100} , {normalization_factor, 1000}}),
|
||||
std::move(current_backlog)
|
||||
)
|
||||
{}
|
||||
|
||||
4
bytes.cc
4
bytes.cc
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "bytes.hh"
|
||||
#include <seastar/core/print.hh>
|
||||
#include "core/print.hh"
|
||||
|
||||
static inline int8_t hex_to_int(unsigned char c) {
|
||||
switch (c) {
|
||||
@@ -55,7 +55,7 @@ bytes from_hex(sstring_view s) {
|
||||
auto half_byte1 = hex_to_int(s[i * 2]);
|
||||
auto half_byte2 = hex_to_int(s[i * 2 + 1]);
|
||||
if (half_byte1 == -1 || half_byte2 == -1) {
|
||||
throw std::invalid_argument(format("Non-hex characters in {}", s));
|
||||
throw std::invalid_argument(sprint("Non-hex characters in %s", s));
|
||||
}
|
||||
out[i] = (half_byte1 << 4) | half_byte2;
|
||||
}
|
||||
|
||||
24
bytes.hh
24
bytes.hh
@@ -22,22 +22,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "seastarx.hh"
|
||||
#include <seastar/core/sstring.hh>
|
||||
#include "core/sstring.hh"
|
||||
#include "hashing.hh"
|
||||
#include <optional>
|
||||
#include <experimental/optional>
|
||||
#include <iosfwd>
|
||||
#include <functional>
|
||||
#include "utils/mutable_view.hh"
|
||||
|
||||
using bytes = basic_sstring<int8_t, uint32_t, 31, false>;
|
||||
using bytes_view = std::basic_string_view<int8_t>;
|
||||
using bytes = basic_sstring<int8_t, uint32_t, 31>;
|
||||
using bytes_view = std::experimental::basic_string_view<int8_t>;
|
||||
using bytes_mutable_view = basic_mutable_view<bytes_view::value_type>;
|
||||
using bytes_opt = std::optional<bytes>;
|
||||
using sstring_view = std::string_view;
|
||||
|
||||
inline sstring_view to_sstring_view(bytes_view view) {
|
||||
return {reinterpret_cast<const char*>(view.data()), view.size()};
|
||||
}
|
||||
using bytes_opt = std::experimental::optional<bytes>;
|
||||
using sstring_view = std::experimental::string_view;
|
||||
|
||||
namespace std {
|
||||
|
||||
@@ -82,11 +78,3 @@ struct appending_hash<bytes_view> {
|
||||
h.update(reinterpret_cast<const char*>(v.begin()), v.size() * sizeof(bytes_view::value_type));
|
||||
}
|
||||
};
|
||||
|
||||
inline int32_t compare_unsigned(bytes_view v1, bytes_view v2) {
|
||||
auto n = memcmp(v1.begin(), v2.begin(), std::min(v1.size(), v2.size()));
|
||||
if (n) {
|
||||
return n;
|
||||
}
|
||||
return (int32_t) (v1.size() - v2.size());
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include "bytes.hh"
|
||||
#include <seastar/core/unaligned.hh>
|
||||
#include "core/unaligned.hh"
|
||||
#include "hashing.hh"
|
||||
#include <seastar/core/simple-stream.hh>
|
||||
#include "seastar/core/simple-stream.hh"
|
||||
/**
|
||||
* Utility for writing data into a buffer when its final size is not known up front.
|
||||
*
|
||||
@@ -38,7 +38,7 @@ class bytes_ostream {
|
||||
public:
|
||||
using size_type = bytes::size_type;
|
||||
using value_type = bytes::value_type;
|
||||
static constexpr size_type max_chunk_size() { return 128 * 1024; }
|
||||
static constexpr size_type max_chunk_size() { return 16 * 1024; }
|
||||
private:
|
||||
static_assert(sizeof(value_type) == 1, "value_type is assumed to be one byte long");
|
||||
struct chunk {
|
||||
@@ -57,17 +57,16 @@ private:
|
||||
value_type data[0];
|
||||
void operator delete(void* ptr) { free(ptr); }
|
||||
};
|
||||
static constexpr size_type default_chunk_size{512};
|
||||
// FIXME: consider increasing chunk size as the buffer grows
|
||||
static constexpr size_type chunk_size{512};
|
||||
private:
|
||||
std::unique_ptr<chunk> _begin;
|
||||
chunk* _current;
|
||||
size_type _size;
|
||||
size_type _initial_chunk_size = default_chunk_size;
|
||||
public:
|
||||
class fragment_iterator : public std::iterator<std::input_iterator_tag, bytes_view> {
|
||||
chunk* _current = nullptr;
|
||||
chunk* _current;
|
||||
public:
|
||||
fragment_iterator() = default;
|
||||
fragment_iterator(chunk* current) : _current(current) {}
|
||||
fragment_iterator(const fragment_iterator&) = default;
|
||||
fragment_iterator& operator=(const fragment_iterator&) = default;
|
||||
@@ -102,13 +101,13 @@ private:
|
||||
}
|
||||
// Figure out next chunk size.
|
||||
// - must be enough for data_size
|
||||
// - must be at least _initial_chunk_size
|
||||
// - must be at least chunk_size
|
||||
// - try to double each time to prevent too many allocations
|
||||
// - do not exceed max_chunk_size
|
||||
size_type next_alloc_size(size_t data_size) const {
|
||||
auto next_size = _current
|
||||
? _current->size * 2
|
||||
: _initial_chunk_size;
|
||||
: chunk_size;
|
||||
next_size = std::min(next_size, max_chunk_size());
|
||||
// FIXME: check for overflow?
|
||||
return std::max<size_type>(next_size, data_size + sizeof(chunk));
|
||||
@@ -116,19 +115,13 @@ private:
|
||||
// Makes room for a contiguous region of given size.
|
||||
// The region is accounted for as already written.
|
||||
// size must not be zero.
|
||||
[[gnu::always_inline]]
|
||||
value_type* alloc(size_type size) {
|
||||
if (__builtin_expect(size <= current_space_left(), true)) {
|
||||
if (size <= current_space_left()) {
|
||||
auto ret = _current->data + _current->offset;
|
||||
_current->offset += size;
|
||||
_size += size;
|
||||
return ret;
|
||||
} else {
|
||||
return alloc_new(size);
|
||||
}
|
||||
}
|
||||
[[gnu::noinline]]
|
||||
value_type* alloc_new(size_type size) {
|
||||
auto alloc_size = next_alloc_size(size);
|
||||
auto space = malloc(alloc_size);
|
||||
if (!space) {
|
||||
@@ -146,22 +139,19 @@ private:
|
||||
}
|
||||
_size += size;
|
||||
return _current->data;
|
||||
};
|
||||
}
|
||||
public:
|
||||
explicit bytes_ostream(size_t initial_chunk_size) noexcept
|
||||
bytes_ostream() noexcept
|
||||
: _begin()
|
||||
, _current(nullptr)
|
||||
, _size(0)
|
||||
, _initial_chunk_size(initial_chunk_size)
|
||||
{ }
|
||||
|
||||
bytes_ostream() noexcept : bytes_ostream(default_chunk_size) {}
|
||||
|
||||
bytes_ostream(bytes_ostream&& o) noexcept
|
||||
: _begin(std::move(o._begin))
|
||||
, _current(o._current)
|
||||
, _size(o._size)
|
||||
, _initial_chunk_size(o._initial_chunk_size)
|
||||
{
|
||||
o._current = nullptr;
|
||||
o._size = 0;
|
||||
@@ -171,7 +161,6 @@ public:
|
||||
: _begin()
|
||||
, _current(nullptr)
|
||||
, _size(0)
|
||||
, _initial_chunk_size(o._initial_chunk_size)
|
||||
{
|
||||
append(o);
|
||||
}
|
||||
@@ -209,20 +198,18 @@ public:
|
||||
return place_holder<T>{alloc(sizeof(T))};
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
value_type* write_place_holder(size_type size) {
|
||||
return alloc(size);
|
||||
}
|
||||
|
||||
// Writes given sequence of bytes
|
||||
[[gnu::always_inline]]
|
||||
inline void write(bytes_view v) {
|
||||
if (v.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto this_size = std::min(v.size(), size_t(current_space_left()));
|
||||
if (__builtin_expect(this_size, true)) {
|
||||
if (this_size) {
|
||||
memcpy(_current->data + _current->offset, v.begin(), this_size);
|
||||
_current->offset += this_size;
|
||||
_size += this_size;
|
||||
@@ -231,12 +218,11 @@ public:
|
||||
|
||||
while (!v.empty()) {
|
||||
auto this_size = std::min(v.size(), size_t(max_chunk_size()));
|
||||
std::copy_n(v.begin(), this_size, alloc_new(this_size));
|
||||
std::copy_n(v.begin(), this_size, alloc(this_size));
|
||||
v.remove_prefix(this_size);
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
void write(const char* ptr, size_t size) {
|
||||
write(bytes_view(reinterpret_cast<const signed char*>(ptr), size));
|
||||
}
|
||||
@@ -303,24 +289,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Removes n bytes from the end of the bytes_ostream.
|
||||
// Beware of O(n) algorithm.
|
||||
void remove_suffix(size_t n) {
|
||||
_size -= n;
|
||||
auto left = _size;
|
||||
auto current = _begin.get();
|
||||
while (current) {
|
||||
if (current->offset >= left) {
|
||||
current->offset = left;
|
||||
_current = current;
|
||||
current->next.reset();
|
||||
return;
|
||||
}
|
||||
left -= current->offset;
|
||||
current = current->next.get();
|
||||
}
|
||||
}
|
||||
|
||||
// begin() and end() form an input range to bytes_view representing fragments.
|
||||
// Any modification of this instance invalidates iterators.
|
||||
fragment_iterator begin() const { return { _begin.get() }; }
|
||||
@@ -406,19 +374,14 @@ public:
|
||||
bool operator!=(const bytes_ostream& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
// Makes this instance empty.
|
||||
//
|
||||
// The first buffer is not deallocated, so callers may rely on the
|
||||
// fact that if they write less than the initial chunk size between
|
||||
// the clear() calls then writes will not involve any memory allocations,
|
||||
// except for the first write made on this instance.
|
||||
void clear() {
|
||||
if (_begin) {
|
||||
_begin->offset = 0;
|
||||
_size = 0;
|
||||
_current = _begin.get();
|
||||
_begin->next.reset();
|
||||
template<>
|
||||
struct appending_hash<bytes_ostream> {
|
||||
template<typename Hasher>
|
||||
void operator()(Hasher& h, const bytes_ostream& b) const {
|
||||
for (auto&& frag : b.fragments()) {
|
||||
feed_hash(h, frag);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -61,12 +61,11 @@ class cache_flat_mutation_reader final : public flat_mutation_reader::impl {
|
||||
// - _last_row points at a direct predecessor of the next row which is going to be read.
|
||||
// Used for populating continuity.
|
||||
// - _population_range_starts_before_all_rows is set accordingly
|
||||
// - _underlying is engaged and fast-forwarded
|
||||
reading_from_underlying,
|
||||
|
||||
end_of_stream
|
||||
};
|
||||
partition_snapshot_ptr _snp;
|
||||
lw_shared_ptr<partition_snapshot> _snp;
|
||||
position_in_partition::tri_compare _position_cmp;
|
||||
|
||||
query::clustering_key_filter_ranges _ck_ranges;
|
||||
@@ -95,18 +94,7 @@ class cache_flat_mutation_reader final : public flat_mutation_reader::impl {
|
||||
// Valid when _state == reading_from_underlying.
|
||||
bool _population_range_starts_before_all_rows;
|
||||
|
||||
// Whether _lower_bound was changed within current fill_buffer().
|
||||
// If it did not then we cannot break out of it (e.g. on preemption) because
|
||||
// forward progress is not guaranteed in case iterators are getting constantly invalidated.
|
||||
bool _lower_bound_changed = false;
|
||||
|
||||
// Points to the underlying reader conforming to _schema,
|
||||
// either to *_underlying_holder or _read_context->underlying().underlying().
|
||||
flat_mutation_reader* _underlying = nullptr;
|
||||
std::optional<flat_mutation_reader> _underlying_holder;
|
||||
|
||||
future<> do_fill_buffer(db::timeout_clock::time_point);
|
||||
future<> ensure_underlying(db::timeout_clock::time_point);
|
||||
void copy_from_cache_to_buffer();
|
||||
future<> process_static_row(db::timeout_clock::time_point);
|
||||
void move_to_end();
|
||||
@@ -144,7 +132,7 @@ public:
|
||||
dht::decorated_key dk,
|
||||
query::clustering_key_filter_ranges&& crr,
|
||||
lw_shared_ptr<read_context> ctx,
|
||||
partition_snapshot_ptr snp,
|
||||
lw_shared_ptr<partition_snapshot> snp,
|
||||
row_cache& cache)
|
||||
: flat_mutation_reader::impl(std::move(s))
|
||||
, _snp(std::move(snp))
|
||||
@@ -164,6 +152,9 @@ public:
|
||||
cache_flat_mutation_reader(const cache_flat_mutation_reader&) = delete;
|
||||
cache_flat_mutation_reader(cache_flat_mutation_reader&&) = delete;
|
||||
virtual future<> fill_buffer(db::timeout_clock::time_point timeout) override;
|
||||
virtual ~cache_flat_mutation_reader() {
|
||||
maybe_merge_versions(_snp, _lsa_manager.region(), _lsa_manager.read_section());
|
||||
}
|
||||
virtual void next_partition() override {
|
||||
clear_buffer_to_next_partition();
|
||||
if (is_buffer_empty()) {
|
||||
@@ -193,22 +184,23 @@ future<> cache_flat_mutation_reader::process_static_row(db::timeout_clock::time_
|
||||
return make_ready_future<>();
|
||||
} else {
|
||||
_read_context->cache().on_row_miss();
|
||||
return ensure_underlying(timeout).then([this, timeout] {
|
||||
return (*_underlying)(timeout).then([this] (mutation_fragment_opt&& sr) {
|
||||
if (sr) {
|
||||
assert(sr->is_static_row());
|
||||
maybe_add_to_cache(sr->as_static_row());
|
||||
push_mutation_fragment(std::move(*sr));
|
||||
}
|
||||
maybe_set_static_row_continuous();
|
||||
});
|
||||
return _read_context->get_next_fragment(timeout).then([this] (mutation_fragment_opt&& sr) {
|
||||
if (sr) {
|
||||
assert(sr->is_static_row());
|
||||
maybe_add_to_cache(sr->as_static_row());
|
||||
push_mutation_fragment(std::move(*sr));
|
||||
}
|
||||
maybe_set_static_row_continuous();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void cache_flat_mutation_reader::touch_partition() {
|
||||
_snp->touch();
|
||||
if (_snp->at_latest_version()) {
|
||||
rows_entry& last_dummy = *_snp->version()->partition().clustered_rows().rbegin();
|
||||
_snp->tracker()->touch(last_dummy);
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -238,36 +230,14 @@ future<> cache_flat_mutation_reader::fill_buffer(db::timeout_clock::time_point t
|
||||
});
|
||||
}
|
||||
|
||||
inline
|
||||
future<> cache_flat_mutation_reader::ensure_underlying(db::timeout_clock::time_point timeout) {
|
||||
if (_underlying) {
|
||||
return make_ready_future<>();
|
||||
}
|
||||
return _read_context->ensure_underlying(timeout).then([this, timeout] {
|
||||
flat_mutation_reader& ctx_underlying = _read_context->underlying().underlying();
|
||||
if (ctx_underlying.schema() != _schema) {
|
||||
_underlying_holder = make_delegating_reader(ctx_underlying);
|
||||
_underlying_holder->upgrade_schema(_schema);
|
||||
_underlying = &*_underlying_holder;
|
||||
} else {
|
||||
_underlying = &ctx_underlying;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
inline
|
||||
future<> cache_flat_mutation_reader::do_fill_buffer(db::timeout_clock::time_point timeout) {
|
||||
if (_state == state::move_to_underlying) {
|
||||
if (!_underlying) {
|
||||
return ensure_underlying(timeout).then([this, timeout] {
|
||||
return do_fill_buffer(timeout);
|
||||
});
|
||||
}
|
||||
_state = state::reading_from_underlying;
|
||||
_population_range_starts_before_all_rows = _lower_bound.is_before_all_clustered_rows(*_schema);
|
||||
auto end = _next_row_in_range ? position_in_partition(_next_row.position())
|
||||
: position_in_partition(_upper_bound);
|
||||
return _underlying->fast_forward_to(position_range{_lower_bound, std::move(end)}, timeout).then([this, timeout] {
|
||||
return _read_context->fast_forward_to(position_range{_lower_bound, std::move(end)}, timeout).then([this, timeout] {
|
||||
return read_from_underlying(timeout);
|
||||
});
|
||||
}
|
||||
@@ -292,13 +262,9 @@ future<> cache_flat_mutation_reader::do_fill_buffer(db::timeout_clock::time_poin
|
||||
}
|
||||
_next_row.maybe_refresh();
|
||||
clogger.trace("csm {}: next={}, cont={}", this, _next_row.position(), _next_row.continuous());
|
||||
_lower_bound_changed = false;
|
||||
while (_state == state::reading_from_cache) {
|
||||
while (!is_buffer_full() && _state == state::reading_from_cache) {
|
||||
copy_from_cache_to_buffer();
|
||||
// We need to check _lower_bound_changed even if is_buffer_full() because
|
||||
// we may have emitted only a range tombstone which overlapped with _lower_bound
|
||||
// and thus didn't cause _lower_bound to change.
|
||||
if ((need_preempt() || is_buffer_full()) && _lower_bound_changed) {
|
||||
if (need_preempt()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -308,7 +274,7 @@ future<> cache_flat_mutation_reader::do_fill_buffer(db::timeout_clock::time_poin
|
||||
|
||||
inline
|
||||
future<> cache_flat_mutation_reader::read_from_underlying(db::timeout_clock::time_point timeout) {
|
||||
return consume_mutation_fragments_until(*_underlying,
|
||||
return consume_mutation_fragments_until(_read_context->underlying().underlying(),
|
||||
[this] { return _state != state::reading_from_underlying || is_buffer_full(); },
|
||||
[this] (mutation_fragment mf) {
|
||||
_read_context->cache().on_row_miss();
|
||||
@@ -389,7 +355,7 @@ future<> cache_flat_mutation_reader::read_from_underlying(db::timeout_clock::tim
|
||||
}
|
||||
});
|
||||
return make_ready_future<>();
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -408,7 +374,7 @@ bool cache_flat_mutation_reader::ensure_population_lower_bound() {
|
||||
rows_entry::compare less(*_schema);
|
||||
// FIXME: Avoid the copy by inserting an incomplete clustering row
|
||||
auto e = alloc_strategy_unique_ptr<rows_entry>(
|
||||
current_allocator().construct<rows_entry>(*_schema, *_last_row));
|
||||
current_allocator().construct<rows_entry>(*_last_row));
|
||||
e->set_continuous(false);
|
||||
auto insert_result = rows.insert_check(rows.end(), *e, less);
|
||||
auto inserted = insert_result.second;
|
||||
@@ -453,7 +419,7 @@ void cache_flat_mutation_reader::maybe_add_to_cache(const clustering_row& cr) {
|
||||
_read_context->cache().on_mispopulate();
|
||||
return;
|
||||
}
|
||||
clogger.trace("csm {}: populate({})", this, clustering_row::printer(*_schema, cr));
|
||||
clogger.trace("csm {}: populate({})", this, cr);
|
||||
_lsa_manager.run_in_update_section_with_allocator([this, &cr] {
|
||||
mutation_partition& mp = _snp->version()->partition();
|
||||
rows_entry::compare less(*_schema);
|
||||
@@ -462,7 +428,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.tomb(), cr.marker(), cr.cells()));
|
||||
current_allocator().construct<rows_entry>(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);
|
||||
@@ -505,19 +471,15 @@ void cache_flat_mutation_reader::copy_from_cache_to_buffer() {
|
||||
_next_row.touch();
|
||||
position_in_partition_view next_lower_bound = _next_row.dummy() ? _next_row.position() : position_in_partition_view::after_key(_next_row.key());
|
||||
for (auto &&rts : _snp->range_tombstones(_lower_bound, _next_row_in_range ? next_lower_bound : _upper_bound)) {
|
||||
position_in_partition::less_compare less(*_schema);
|
||||
// This guarantees that rts starts after any emitted clustering_row
|
||||
// and not before any emitted range tombstone.
|
||||
if (!less(_lower_bound, rts.position())) {
|
||||
rts.set_start(*_schema, _lower_bound);
|
||||
} else {
|
||||
if (rts.trim_front(*_schema, _lower_bound)) {
|
||||
_lower_bound = position_in_partition(rts.position());
|
||||
_lower_bound_changed = true;
|
||||
if (is_buffer_full()) {
|
||||
return;
|
||||
}
|
||||
push_mutation_fragment(std::move(rts));
|
||||
}
|
||||
push_mutation_fragment(std::move(rts));
|
||||
}
|
||||
// We add the row to the buffer even when it's full.
|
||||
// This simplifies the code. For more info see #3139.
|
||||
@@ -554,7 +516,6 @@ void cache_flat_mutation_reader::move_to_range(query::clustering_row_ranges::con
|
||||
_last_row = nullptr;
|
||||
_lower_bound = std::move(lb);
|
||||
_upper_bound = std::move(ub);
|
||||
_lower_bound_changed = true;
|
||||
_ck_ranges_curr = next_it;
|
||||
auto adjacent = _next_row.advance_to(_lower_bound);
|
||||
_next_row_in_range = !after_current_range(_next_row.position());
|
||||
@@ -605,7 +566,7 @@ void cache_flat_mutation_reader::move_to_next_entry() {
|
||||
|
||||
inline
|
||||
void cache_flat_mutation_reader::add_to_buffer(mutation_fragment&& mf) {
|
||||
clogger.trace("csm {}: add_to_buffer({})", this, mutation_fragment::printer(*_schema, mf));
|
||||
clogger.trace("csm {}: add_to_buffer({})", this, mf);
|
||||
if (mf.is_clustering_row()) {
|
||||
add_clustering_row_to_buffer(std::move(mf));
|
||||
} else {
|
||||
@@ -627,12 +588,11 @@ void cache_flat_mutation_reader::add_to_buffer(const partition_snapshot_row_curs
|
||||
// (2) If _lower_bound > mf.position(), mf was emitted
|
||||
inline
|
||||
void cache_flat_mutation_reader::add_clustering_row_to_buffer(mutation_fragment&& mf) {
|
||||
clogger.trace("csm {}: add_clustering_row_to_buffer({})", this, mutation_fragment::printer(*_schema, mf));
|
||||
clogger.trace("csm {}: add_clustering_row_to_buffer({})", this, mf);
|
||||
auto& row = mf.as_clustering_row();
|
||||
auto new_lower_bound = position_in_partition::after_key(row.key());
|
||||
push_mutation_fragment(std::move(mf));
|
||||
_lower_bound = std::move(new_lower_bound);
|
||||
_lower_bound_changed = true;
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -640,16 +600,10 @@ void cache_flat_mutation_reader::add_to_buffer(range_tombstone&& rt) {
|
||||
clogger.trace("csm {}: add_to_buffer({})", this, rt);
|
||||
// This guarantees that rt starts after any emitted clustering_row
|
||||
// and not before any emitted range tombstone.
|
||||
position_in_partition::less_compare less(*_schema);
|
||||
if (!less(_lower_bound, rt.end_position())) {
|
||||
if (!rt.trim_front(*_schema, _lower_bound)) {
|
||||
return;
|
||||
}
|
||||
if (!less(_lower_bound, rt.position())) {
|
||||
rt.set_start(*_schema, _lower_bound);
|
||||
} else {
|
||||
_lower_bound = position_in_partition(rt.position());
|
||||
_lower_bound_changed = true;
|
||||
}
|
||||
_lower_bound = position_in_partition(rt.position());
|
||||
push_mutation_fragment(std::move(rt));
|
||||
}
|
||||
|
||||
@@ -668,7 +622,7 @@ void cache_flat_mutation_reader::maybe_add_to_cache(const range_tombstone& rt) {
|
||||
inline
|
||||
void cache_flat_mutation_reader::maybe_add_to_cache(const static_row& sr) {
|
||||
if (can_populate()) {
|
||||
clogger.trace("csm {}: populate({})", this, static_row::printer(*_schema, sr));
|
||||
clogger.trace("csm {}: populate({})", this, sr);
|
||||
_read_context->cache().on_static_row_insert();
|
||||
_lsa_manager.run_in_update_section_with_allocator([&] {
|
||||
if (_read_context->digest_requested()) {
|
||||
@@ -703,7 +657,7 @@ inline flat_mutation_reader make_cache_flat_mutation_reader(schema_ptr s,
|
||||
query::clustering_key_filter_ranges crr,
|
||||
row_cache& cache,
|
||||
lw_shared_ptr<cache::read_context> ctx,
|
||||
partition_snapshot_ptr snp)
|
||||
lw_shared_ptr<partition_snapshot> snp)
|
||||
{
|
||||
return make_flat_mutation_reader<cache::cache_flat_mutation_reader>(
|
||||
std::move(s), std::move(dk), std::move(crr), std::move(ctx), std::move(snp), cache);
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ser {
|
||||
|
||||
template <typename T>
|
||||
class serializer;
|
||||
|
||||
};
|
||||
|
||||
class cache_temperature {
|
||||
float hit_rate;
|
||||
explicit cache_temperature(uint8_t hr) : hit_rate(hr/255.0f) {}
|
||||
public:
|
||||
uint8_t get_serialized_temperature() const {
|
||||
return hit_rate * 255;
|
||||
}
|
||||
cache_temperature() : hit_rate(0) {}
|
||||
explicit cache_temperature(float hr) : hit_rate(hr) {}
|
||||
explicit operator float() const { return hit_rate; }
|
||||
static cache_temperature invalid() { return cache_temperature(-1.0f); }
|
||||
friend struct ser::serializer<cache_temperature>;
|
||||
};
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <seastar/core/sstring.hh>
|
||||
#include <core/sstring.hh>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "json.hh"
|
||||
|
||||
@@ -68,7 +68,7 @@ mutation canonical_mutation::to_mutation(schema_ptr s) const {
|
||||
|
||||
auto cf_id = mv.table_id();
|
||||
if (s->id() != cf_id) {
|
||||
throw std::runtime_error(format("Attempted to deserialize canonical_mutation of table {} with schema of table {} ({}.{})",
|
||||
throw std::runtime_error(sprint("Attempted to deserialize canonical_mutation of table %s with schema of table %s (%s.%s)",
|
||||
cf_id, s->id(), s->ks_name(), s->cf_name()));
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,29 @@
|
||||
|
||||
#include <boost/intrusive/unordered_set.hpp>
|
||||
|
||||
#include "utils/small_vector.hh"
|
||||
#if __has_include(<boost/container/small_vector.hpp>)
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
template <typename T, size_t N>
|
||||
using small_vector = boost::container::small_vector<T, N>;
|
||||
|
||||
#else
|
||||
|
||||
#include <vector>
|
||||
template <typename T, size_t N>
|
||||
using small_vector = std::vector<T>;
|
||||
|
||||
#endif
|
||||
|
||||
#include "fnv1a_hasher.hh"
|
||||
#include "mutation_fragment.hh"
|
||||
#include "mutation_partition.hh"
|
||||
#include "xx_hasher.hh"
|
||||
|
||||
#include "db/timeout_clock.hh"
|
||||
|
||||
class cells_range {
|
||||
using ids_vector_type = utils::small_vector<column_id, 5>;
|
||||
using ids_vector_type = small_vector<column_id, 5>;
|
||||
|
||||
position_in_partition_view _position;
|
||||
ids_vector_type _ids;
|
||||
@@ -63,7 +77,7 @@ class partition_cells_range {
|
||||
public:
|
||||
class iterator {
|
||||
const mutation_partition& _mp;
|
||||
std::optional<mutation_partition::rows_type::const_iterator> _position;
|
||||
stdx::optional<mutation_partition::rows_type::const_iterator> _position;
|
||||
cells_range _current;
|
||||
public:
|
||||
explicit iterator(const mutation_partition& mp)
|
||||
@@ -194,10 +208,10 @@ private:
|
||||
explicit hasher(const schema& s) : _schema(&s) { }
|
||||
|
||||
size_t operator()(const cell_address& ca) const {
|
||||
xx_hasher hasher;
|
||||
fnv1a_hasher hasher;
|
||||
ca.position.feed_hash(hasher, *_schema);
|
||||
::feed_hash(hasher, ca.id);
|
||||
return static_cast<size_t>(hasher.finalize_uint64());
|
||||
return hasher.finalize();
|
||||
}
|
||||
size_t operator()(const cell_entry& ce) const {
|
||||
return operator()(ce._address);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "keys.hh"
|
||||
#include "schema.hh"
|
||||
#include "range.hh"
|
||||
@@ -44,20 +43,22 @@ bound_kind invert_kind(bound_kind k);
|
||||
int32_t weight(bound_kind k);
|
||||
|
||||
class bound_view {
|
||||
const static thread_local clustering_key _empty_prefix;
|
||||
std::reference_wrapper<const clustering_key_prefix> _prefix;
|
||||
bound_kind _kind;
|
||||
public:
|
||||
const static thread_local clustering_key empty_prefix;
|
||||
const clustering_key_prefix& prefix;
|
||||
bound_kind kind;
|
||||
bound_view(const clustering_key_prefix& prefix, bound_kind kind)
|
||||
: _prefix(prefix)
|
||||
, _kind(kind)
|
||||
: prefix(prefix)
|
||||
, kind(kind)
|
||||
{ }
|
||||
bound_view(const bound_view& other) noexcept = default;
|
||||
bound_view& operator=(const bound_view& other) noexcept = default;
|
||||
|
||||
bound_kind kind() const { return _kind; }
|
||||
const clustering_key_prefix& prefix() const { return _prefix; }
|
||||
|
||||
bound_view& operator=(const bound_view& other) noexcept {
|
||||
if (this != &other) {
|
||||
this->~bound_view();
|
||||
new (this) bound_view(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
struct tri_compare {
|
||||
// To make it assignable and to avoid taking a schema_ptr, we
|
||||
// wrap the schema reference.
|
||||
@@ -81,13 +82,13 @@ public:
|
||||
return d1 < d2 ? w1 - (w1 <= 0) : -(w2 - (w2 <= 0));
|
||||
}
|
||||
int operator()(const bound_view b, const clustering_key_prefix& p) const {
|
||||
return operator()(b._prefix, weight(b._kind), p, 0);
|
||||
return operator()(b.prefix, weight(b.kind), p, 0);
|
||||
}
|
||||
int operator()(const clustering_key_prefix& p, const bound_view b) const {
|
||||
return operator()(p, 0, b._prefix, weight(b._kind));
|
||||
return operator()(p, 0, b.prefix, weight(b.kind));
|
||||
}
|
||||
int operator()(const bound_view b1, const bound_view b2) const {
|
||||
return operator()(b1._prefix, weight(b1._kind), b2._prefix, weight(b2._kind));
|
||||
return operator()(b1.prefix, weight(b1.kind), b2.prefix, weight(b2.kind));
|
||||
}
|
||||
};
|
||||
struct compare {
|
||||
@@ -100,26 +101,26 @@ public:
|
||||
return _cmp(p1, w1, p2, w2) < 0;
|
||||
}
|
||||
bool operator()(const bound_view b, const clustering_key_prefix& p) const {
|
||||
return operator()(b._prefix, weight(b._kind), p, 0);
|
||||
return operator()(b.prefix, weight(b.kind), p, 0);
|
||||
}
|
||||
bool operator()(const clustering_key_prefix& p, const bound_view b) const {
|
||||
return operator()(p, 0, b._prefix, weight(b._kind));
|
||||
return operator()(p, 0, b.prefix, weight(b.kind));
|
||||
}
|
||||
bool operator()(const bound_view b1, const bound_view b2) const {
|
||||
return operator()(b1._prefix, weight(b1._kind), b2._prefix, weight(b2._kind));
|
||||
return operator()(b1.prefix, weight(b1.kind), b2.prefix, weight(b2.kind));
|
||||
}
|
||||
};
|
||||
bool equal(const schema& s, const bound_view other) const {
|
||||
return _kind == other._kind && _prefix.get().equal(s, other._prefix.get());
|
||||
return kind == other.kind && prefix.equal(s, other.prefix);
|
||||
}
|
||||
bool adjacent(const schema& s, const bound_view other) const {
|
||||
return invert_kind(other._kind) == _kind && _prefix.get().equal(s, other._prefix.get());
|
||||
return invert_kind(other.kind) == kind && prefix.equal(s, other.prefix);
|
||||
}
|
||||
static bound_view bottom() {
|
||||
return {_empty_prefix, bound_kind::incl_start};
|
||||
return {empty_prefix, bound_kind::incl_start};
|
||||
}
|
||||
static bound_view top() {
|
||||
return {_empty_prefix, bound_kind::incl_end};
|
||||
return {empty_prefix, bound_kind::incl_end};
|
||||
}
|
||||
template<template<typename> typename R>
|
||||
GCC6_CONCEPT( requires Range<R, clustering_key_prefix_view> )
|
||||
@@ -142,14 +143,14 @@ public:
|
||||
}
|
||||
template<template<typename> typename R>
|
||||
GCC6_CONCEPT( requires Range<R, clustering_key_prefix_view> )
|
||||
static std::optional<typename R<clustering_key_prefix_view>::bound> to_range_bound(const bound_view& bv) {
|
||||
if (&bv._prefix.get() == &_empty_prefix) {
|
||||
static stdx::optional<typename R<clustering_key_prefix_view>::bound> to_range_bound(const bound_view& bv) {
|
||||
if (&bv.prefix == &empty_prefix) {
|
||||
return {};
|
||||
}
|
||||
bool inclusive = bv._kind != bound_kind::excl_end && bv._kind != bound_kind::excl_start;
|
||||
return {typename R<clustering_key_prefix_view>::bound(bv._prefix.get().view(), inclusive)};
|
||||
bool inclusive = bv.kind != bound_kind::excl_end && bv.kind != bound_kind::excl_start;
|
||||
return {typename R<clustering_key_prefix_view>::bound(bv.prefix.view(), inclusive)};
|
||||
}
|
||||
friend std::ostream& operator<<(std::ostream& out, const bound_view& b) {
|
||||
return out << "{bound: prefix=" << b._prefix.get() << ", kind=" << b._kind << "}";
|
||||
return out << "{bound: prefix=" << b.prefix << ", kind=" << b.kind << "}";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace query {
|
||||
|
||||
class clustering_key_filter_ranges {
|
||||
clustering_row_ranges _storage;
|
||||
std::reference_wrapper<const clustering_row_ranges> _ref;
|
||||
const clustering_row_ranges& _ref;
|
||||
public:
|
||||
clustering_key_filter_ranges(const clustering_row_ranges& ranges) : _ref(ranges) { }
|
||||
struct reversed { };
|
||||
@@ -39,21 +39,21 @@ public:
|
||||
|
||||
clustering_key_filter_ranges(clustering_key_filter_ranges&& other) noexcept
|
||||
: _storage(std::move(other._storage))
|
||||
, _ref(&other._ref.get() == &other._storage ? _storage : other._ref.get())
|
||||
, _ref(&other._ref == &other._storage ? _storage : other._ref)
|
||||
{ }
|
||||
|
||||
clustering_key_filter_ranges& operator=(clustering_key_filter_ranges&& other) noexcept {
|
||||
if (this != &other) {
|
||||
_storage = std::move(other._storage);
|
||||
_ref = (&other._ref.get() == &other._storage) ? _storage : other._ref.get();
|
||||
this->~clustering_key_filter_ranges();
|
||||
new (this) clustering_key_filter_ranges(std::move(other));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto begin() const { return _ref.get().begin(); }
|
||||
auto end() const { return _ref.get().end(); }
|
||||
bool empty() const { return _ref.get().empty(); }
|
||||
size_t size() const { return _ref.get().size(); }
|
||||
auto begin() const { return _ref.begin(); }
|
||||
auto end() const { return _ref.end(); }
|
||||
bool empty() const { return _ref.empty(); }
|
||||
size_t size() const { return _ref.size(); }
|
||||
const clustering_row_ranges& ranges() const { return _ref; }
|
||||
|
||||
static clustering_key_filter_ranges get_ranges(const schema& schema, const query::partition_slice& slice, const partition_key& key) {
|
||||
|
||||
@@ -31,61 +31,72 @@
|
||||
class clustering_ranges_walker {
|
||||
const schema& _schema;
|
||||
const query::clustering_row_ranges& _ranges;
|
||||
boost::iterator_range<query::clustering_row_ranges::const_iterator> _current_range;
|
||||
query::clustering_row_ranges::const_iterator _current;
|
||||
query::clustering_row_ranges::const_iterator _end;
|
||||
bool _in_current; // next position is known to be >= _current_start
|
||||
bool _with_static_row;
|
||||
position_in_partition_view _current_start;
|
||||
position_in_partition_view _current_end;
|
||||
std::optional<position_in_partition> _trim;
|
||||
stdx::optional<position_in_partition> _trim;
|
||||
size_t _change_counter = 1;
|
||||
private:
|
||||
bool advance_to_next_range() {
|
||||
_in_current = false;
|
||||
if (!_current_start.is_static_row()) {
|
||||
if (!_current_range) {
|
||||
if (_current == _end) {
|
||||
return false;
|
||||
}
|
||||
_current_range.advance_begin(1);
|
||||
++_current;
|
||||
}
|
||||
++_change_counter;
|
||||
if (!_current_range) {
|
||||
if (_current == _end) {
|
||||
_current_end = _current_start = position_in_partition_view::after_all_clustered_rows();
|
||||
return false;
|
||||
}
|
||||
_current_start = position_in_partition_view::for_range_start(_current_range.front());
|
||||
_current_end = position_in_partition_view::for_range_end(_current_range.front());
|
||||
_current_start = position_in_partition_view::for_range_start(*_current);
|
||||
_current_end = position_in_partition_view::for_range_end(*_current);
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_current_positions() {
|
||||
if (!_with_static_row) {
|
||||
if (!_current_range) {
|
||||
public:
|
||||
clustering_ranges_walker(const schema& s, const query::clustering_row_ranges& ranges, bool with_static_row = true)
|
||||
: _schema(s)
|
||||
, _ranges(ranges)
|
||||
, _current(ranges.begin())
|
||||
, _end(ranges.end())
|
||||
, _in_current(with_static_row)
|
||||
, _with_static_row(with_static_row)
|
||||
, _current_start(position_in_partition_view::for_static_row())
|
||||
, _current_end(position_in_partition_view::before_all_clustered_rows())
|
||||
{
|
||||
if (!with_static_row) {
|
||||
if (_current == _end) {
|
||||
_current_start = position_in_partition_view::before_all_clustered_rows();
|
||||
} else {
|
||||
_current_start = position_in_partition_view::for_range_start(_current_range.front());
|
||||
_current_end = position_in_partition_view::for_range_end(_current_range.front());
|
||||
_current_start = position_in_partition_view::for_range_start(*_current);
|
||||
_current_end = position_in_partition_view::for_range_end(*_current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
clustering_ranges_walker(const schema& s, const query::clustering_row_ranges& ranges, bool with_static_row = true)
|
||||
: _schema(s)
|
||||
, _ranges(ranges)
|
||||
, _current_range(ranges)
|
||||
, _in_current(with_static_row)
|
||||
, _with_static_row(with_static_row)
|
||||
, _current_start(position_in_partition_view::for_static_row())
|
||||
, _current_end(position_in_partition_view::before_all_clustered_rows()) {
|
||||
set_current_positions();
|
||||
clustering_ranges_walker(clustering_ranges_walker&& o) noexcept
|
||||
: _schema(o._schema)
|
||||
, _ranges(o._ranges)
|
||||
, _current(o._current)
|
||||
, _end(o._end)
|
||||
, _in_current(o._in_current)
|
||||
, _with_static_row(o._with_static_row)
|
||||
, _current_start(o._current_start)
|
||||
, _current_end(o._current_end)
|
||||
, _trim(std::move(o._trim))
|
||||
, _change_counter(o._change_counter)
|
||||
{ }
|
||||
clustering_ranges_walker& operator=(clustering_ranges_walker&& o) {
|
||||
if (this != &o) {
|
||||
this->~clustering_ranges_walker();
|
||||
new (this) clustering_ranges_walker(std::move(o));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
clustering_ranges_walker(const clustering_ranges_walker&) = delete;
|
||||
clustering_ranges_walker(clustering_ranges_walker&&) = delete;
|
||||
|
||||
clustering_ranges_walker& operator=(const clustering_ranges_walker&) = delete;
|
||||
clustering_ranges_walker& operator=(clustering_ranges_walker&&) = delete;
|
||||
|
||||
// Excludes positions smaller than pos from the ranges.
|
||||
// pos should be monotonic.
|
||||
// No constraints between pos and positions passed to advance_to().
|
||||
@@ -162,15 +173,17 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& rng : _current_range) {
|
||||
auto range_start = position_in_partition_view::for_range_start(rng);
|
||||
auto i = _current;
|
||||
while (i != _end) {
|
||||
auto range_start = position_in_partition_view::for_range_start(*i);
|
||||
if (!less(range_start, end)) {
|
||||
return false;
|
||||
}
|
||||
auto range_end = position_in_partition_view::for_range_end(rng);
|
||||
auto range_end = position_in_partition_view::for_range_end(*i);
|
||||
if (less(start, range_end)) {
|
||||
return true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -178,20 +191,18 @@ public:
|
||||
|
||||
// Returns true if advanced past all contained positions. Any later advance_to() until reset() will return false.
|
||||
bool out_of_range() const {
|
||||
return !_in_current && !_current_range;
|
||||
return !_in_current && _current == _end;
|
||||
}
|
||||
|
||||
// Resets the state of the walker so that advance_to() can be now called for new sequence of positions.
|
||||
// Any range trimmings still hold after this.
|
||||
void reset() {
|
||||
_current_range = _ranges;
|
||||
_in_current = _with_static_row;
|
||||
_current_start = position_in_partition_view::for_static_row();
|
||||
_current_end = position_in_partition_view::before_all_clustered_rows();
|
||||
set_current_positions();
|
||||
++_change_counter;
|
||||
if (_trim) {
|
||||
trim_front(*std::exchange(_trim, {}));
|
||||
auto trim = std::move(_trim);
|
||||
auto ctr = _change_counter;
|
||||
*this = clustering_ranges_walker(_schema, _ranges, _with_static_row);
|
||||
_change_counter = ctr + 1;
|
||||
if (trim) {
|
||||
trim_front(std::move(*trim));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,11 +211,6 @@ public:
|
||||
return _current_start;
|
||||
}
|
||||
|
||||
// Returns the upper bound of the last range in provided ranges set
|
||||
position_in_partition_view uppermost_bound() const {
|
||||
return position_in_partition_view::for_range_end(_ranges.back());
|
||||
}
|
||||
|
||||
// When lower_bound() changes, this also does
|
||||
// Always > 0.
|
||||
size_t lower_bound_change_counter() const {
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "sstables/compaction_backlog_manager.hh"
|
||||
|
||||
class table;
|
||||
using column_family = table;
|
||||
class column_family;
|
||||
class schema;
|
||||
using schema_ptr = lw_shared_ptr<const schema>;
|
||||
|
||||
@@ -61,8 +60,6 @@ public:
|
||||
// Return a list of sstables to be compacted after applying the strategy.
|
||||
compaction_descriptor get_sstables_for_compaction(column_family& cfs, std::vector<shared_sstable> candidates);
|
||||
|
||||
compaction_descriptor get_major_compaction_job(column_family& cf, std::vector<shared_sstable> candidates);
|
||||
|
||||
std::vector<resharding_descriptor> get_resharding_jobs(column_family& cf, std::vector<shared_sstable> candidates);
|
||||
|
||||
// Some strategies may look at the compacted and resulting sstables to
|
||||
@@ -75,9 +72,6 @@ public:
|
||||
// Return if optimization to rule out sstables based on clustering key filter should be applied.
|
||||
bool use_clustering_key_filter() const;
|
||||
|
||||
// Return true if compaction strategy ignores sstables coming from partial runs.
|
||||
bool ignore_partial_runs() const;
|
||||
|
||||
// An estimation of number of compaction for strategy to be satisfied.
|
||||
int64_t estimated_pending_compactions(column_family& cf) const;
|
||||
|
||||
@@ -116,7 +110,7 @@ public:
|
||||
} else if (short_name == "TimeWindowCompactionStrategy") {
|
||||
return compaction_strategy_type::time_window;
|
||||
} else {
|
||||
throw exceptions::configuration_exception(format("Unable to find compaction strategy class '{}'", name));
|
||||
throw exceptions::configuration_exception(sprint("Unable to find compaction strategy class '%s'", name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
67
compatible_ring_position.hh
Normal file
67
compatible_ring_position.hh
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "query-request.hh"
|
||||
#include <experimental/optional>
|
||||
|
||||
// Wraps ring_position so it is compatible with old-style C++: default constructor,
|
||||
// stateless comparators, yada yada
|
||||
class compatible_ring_position {
|
||||
const schema* _schema = nullptr;
|
||||
// optional to supply a default constructor, no more
|
||||
std::experimental::optional<dht::ring_position> _rp;
|
||||
public:
|
||||
compatible_ring_position() noexcept = default;
|
||||
compatible_ring_position(const schema& s, const dht::ring_position& rp)
|
||||
: _schema(&s), _rp(rp) {
|
||||
}
|
||||
compatible_ring_position(const schema& s, dht::ring_position&& rp)
|
||||
: _schema(&s), _rp(std::move(rp)) {
|
||||
}
|
||||
const dht::token& token() const {
|
||||
return _rp->token();
|
||||
}
|
||||
friend int tri_compare(const compatible_ring_position& x, const compatible_ring_position& y) {
|
||||
return x._rp->tri_compare(*x._schema, *y._rp);
|
||||
}
|
||||
friend bool operator<(const compatible_ring_position& x, const compatible_ring_position& y) {
|
||||
return tri_compare(x, y) < 0;
|
||||
}
|
||||
friend bool operator<=(const compatible_ring_position& x, const compatible_ring_position& y) {
|
||||
return tri_compare(x, y) <= 0;
|
||||
}
|
||||
friend bool operator>(const compatible_ring_position& x, const compatible_ring_position& y) {
|
||||
return tri_compare(x, y) > 0;
|
||||
}
|
||||
friend bool operator>=(const compatible_ring_position& x, const compatible_ring_position& y) {
|
||||
return tri_compare(x, y) >= 0;
|
||||
}
|
||||
friend bool operator==(const compatible_ring_position& x, const compatible_ring_position& y) {
|
||||
return tri_compare(x, y) == 0;
|
||||
}
|
||||
friend bool operator!=(const compatible_ring_position& x, const compatible_ring_position& y) {
|
||||
return tri_compare(x, y) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "query-request.hh"
|
||||
#include <optional>
|
||||
|
||||
// Wraps ring_position_view so it is compatible with old-style C++: default
|
||||
// constructor, stateless comparators, yada yada.
|
||||
class compatible_ring_position_view {
|
||||
const schema* _schema = nullptr;
|
||||
// Optional to supply a default constructor, no more.
|
||||
std::optional<dht::ring_position_view> _rpv;
|
||||
public:
|
||||
constexpr compatible_ring_position_view() = default;
|
||||
compatible_ring_position_view(const schema& s, dht::ring_position_view rpv)
|
||||
: _schema(&s), _rpv(rpv) {
|
||||
}
|
||||
const dht::ring_position_view& position() const {
|
||||
return *_rpv;
|
||||
}
|
||||
friend int tri_compare(const compatible_ring_position_view& x, const compatible_ring_position_view& y) {
|
||||
return dht::ring_position_tri_compare(*x._schema, *x._rpv, *y._rpv);
|
||||
}
|
||||
friend bool operator<(const compatible_ring_position_view& x, const compatible_ring_position_view& y) {
|
||||
return tri_compare(x, y) < 0;
|
||||
}
|
||||
friend bool operator<=(const compatible_ring_position_view& x, const compatible_ring_position_view& y) {
|
||||
return tri_compare(x, y) <= 0;
|
||||
}
|
||||
friend bool operator>(const compatible_ring_position_view& x, const compatible_ring_position_view& y) {
|
||||
return tri_compare(x, y) > 0;
|
||||
}
|
||||
friend bool operator>=(const compatible_ring_position_view& x, const compatible_ring_position_view& y) {
|
||||
return tri_compare(x, y) >= 0;
|
||||
}
|
||||
friend bool operator==(const compatible_ring_position_view& x, const compatible_ring_position_view& y) {
|
||||
return tri_compare(x, y) == 0;
|
||||
}
|
||||
friend bool operator!=(const compatible_ring_position_view& x, const compatible_ring_position_view& y) {
|
||||
return tri_compare(x, y) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include "utils/serialization.hh"
|
||||
#include <seastar/util/backtrace.hh>
|
||||
#include "util/backtrace.hh"
|
||||
#include "unimplemented.hh"
|
||||
|
||||
enum class allow_prefixes { no, yes };
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
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()));
|
||||
throw std::runtime_error(sprint("Key size too large: %d > %d", size, std::numeric_limits<size_type>::max()));
|
||||
}
|
||||
bytes b(bytes::initialized_later(), size);
|
||||
auto i = b.begin();
|
||||
@@ -145,7 +145,7 @@ public:
|
||||
}
|
||||
len = read_simple<size_type>(_v);
|
||||
if (_v.size() < len) {
|
||||
throw_with_backtrace<marshal_exception>(format("compound_type iterator - not enough bytes, expected {:d}, got {:d}", len, _v.size()));
|
||||
throw_with_backtrace<marshal_exception>(sprint("compound_type iterator - not enough bytes, expected %d, got %d", len, _v.size()));
|
||||
}
|
||||
}
|
||||
_current = bytes_view(_v.begin(), len);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include "compound.hh"
|
||||
#include "schema.hh"
|
||||
#include "sstables/version.hh"
|
||||
|
||||
//
|
||||
// This header provides adaptors between the representation used by our compound_type<>
|
||||
@@ -284,7 +283,7 @@ private:
|
||||
// bytes is the static prefix or not).
|
||||
auto value_size = size(*it);
|
||||
if (value_size > static_cast<size_type>(std::numeric_limits<size_type>::max() - uint8_t(is_compound))) {
|
||||
throw std::runtime_error(format("First component size too large: {:d} > {:d}", value_size, std::numeric_limits<size_type>::max() - is_compound));
|
||||
throw std::runtime_error(sprint("First component size too large: %d > %d", value_size, std::numeric_limits<size_type>::max() - is_compound));
|
||||
}
|
||||
if (!is_compound) {
|
||||
return value_size;
|
||||
@@ -295,7 +294,7 @@ private:
|
||||
for ( ; it != values.end(); ++it) {
|
||||
auto value_size = size(*it);
|
||||
if (value_size > std::numeric_limits<size_type>::max()) {
|
||||
throw std::runtime_error(format("Component size too large: {:d} > {:d}", value_size, std::numeric_limits<size_type>::max()));
|
||||
throw std::runtime_error(sprint("Component size too large: %d > %d", value_size, std::numeric_limits<size_type>::max()));
|
||||
}
|
||||
len += sizeof(size_type) + value_size + sizeof(eoc_type);
|
||||
}
|
||||
@@ -303,7 +302,7 @@ private:
|
||||
}
|
||||
public:
|
||||
template <typename Describer>
|
||||
auto describe_type(sstables::sstable_version_types v, Describer f) const {
|
||||
auto describe_type(Describer f) const {
|
||||
return f(const_cast<bytes&>(_bytes));
|
||||
}
|
||||
|
||||
@@ -346,7 +345,7 @@ public:
|
||||
}
|
||||
len = read_simple<size_type>(_v);
|
||||
if (_v.size() < len) {
|
||||
throw_with_backtrace<marshal_exception>(format("composite iterator - not enough bytes, expected {:d}, got {:d}", len, _v.size()));
|
||||
throw_with_backtrace<marshal_exception>(sprint("composite iterator - not enough bytes, expected %d, got %d", len, _v.size()));
|
||||
}
|
||||
}
|
||||
auto value = bytes_view(_v.begin(), len);
|
||||
@@ -468,7 +467,7 @@ public:
|
||||
|
||||
template <typename Component>
|
||||
friend inline std::ostream& operator<<(std::ostream& os, const std::pair<Component, eoc>& c) {
|
||||
return os << "{value=" << c.first << "; eoc=" << format("0x{:02x}", eoc_type(c.second) & 0xff) << "}";
|
||||
return os << "{value=" << c.first << "; eoc=" << sprint("0x%02x", eoc_type(c.second) & 0xff) << "}";
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const composite& v);
|
||||
@@ -511,7 +510,7 @@ public:
|
||||
auto marker = it->second;
|
||||
++it;
|
||||
if (it != e && marker != composite::eoc::none) {
|
||||
throw runtime_exception(format("non-zero component divider found ({:d}) mid", format("0x{:02x}", composite::eoc_type(marker) & 0xff)));
|
||||
throw runtime_exception(sprint("non-zero component divider found (%d) mid", sprint("0x%02x", composite::eoc_type(marker) & 0xff)));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
22
compress.cc
22
compress.cc
@@ -95,7 +95,7 @@ shared_ptr<compressor> compressor::create(const std::map<sstring, sstring>& opti
|
||||
return create(i->second, [&options](const sstring& key) -> opt_string {
|
||||
auto i = options.find(key);
|
||||
if (i == options.end()) {
|
||||
return std::nullopt;
|
||||
return std::experimental::nullopt;
|
||||
}
|
||||
return { i->second };
|
||||
});
|
||||
@@ -108,12 +108,11 @@ thread_local const shared_ptr<compressor> compressor::snappy = make_shared<snapp
|
||||
thread_local const shared_ptr<compressor> compressor::deflate = make_shared<deflate_processor>(namespace_prefix + "DeflateCompressor");
|
||||
|
||||
const sstring compression_parameters::SSTABLE_COMPRESSION = "sstable_compression";
|
||||
const sstring compression_parameters::CHUNK_LENGTH_KB = "chunk_length_in_kb";
|
||||
const sstring compression_parameters::CHUNK_LENGTH_KB_ERR = "chunk_length_kb";
|
||||
const sstring compression_parameters::CHUNK_LENGTH_KB = "chunk_length_kb";
|
||||
const sstring compression_parameters::CRC_CHECK_CHANCE = "crc_check_chance";
|
||||
|
||||
compression_parameters::compression_parameters()
|
||||
: compression_parameters(compressor::lz4)
|
||||
: compression_parameters(nullptr)
|
||||
{}
|
||||
|
||||
compression_parameters::~compression_parameters()
|
||||
@@ -128,14 +127,12 @@ compression_parameters::compression_parameters(const std::map<sstring, sstring>&
|
||||
|
||||
validate_options(options);
|
||||
|
||||
auto chunk_length = options.find(CHUNK_LENGTH_KB) != options.end() ?
|
||||
options.find(CHUNK_LENGTH_KB) : options.find(CHUNK_LENGTH_KB_ERR);
|
||||
|
||||
auto chunk_length = options.find(CHUNK_LENGTH_KB);
|
||||
if (chunk_length != options.end()) {
|
||||
try {
|
||||
_chunk_length = std::stoi(chunk_length->second) * 1024;
|
||||
} catch (const std::exception& e) {
|
||||
throw exceptions::syntax_exception(sstring("Invalid integer value ") + chunk_length->second + " for " + chunk_length->first);
|
||||
throw exceptions::syntax_exception(sstring("Invalid integer value ") + chunk_length->second + " for " + CHUNK_LENGTH_KB);
|
||||
}
|
||||
}
|
||||
auto crc_chance = options.find(CRC_CHECK_CHANCE);
|
||||
@@ -152,13 +149,11 @@ void compression_parameters::validate() {
|
||||
if (_chunk_length) {
|
||||
auto chunk_length = _chunk_length.value();
|
||||
if (chunk_length <= 0) {
|
||||
throw exceptions::configuration_exception(
|
||||
fmt::sprintf("Invalid negative or null for %s/%s", CHUNK_LENGTH_KB, CHUNK_LENGTH_KB_ERR));
|
||||
throw exceptions::configuration_exception(sstring("Invalid negative or null ") + CHUNK_LENGTH_KB);
|
||||
}
|
||||
// _chunk_length must be a power of two
|
||||
if (chunk_length & (chunk_length - 1)) {
|
||||
throw exceptions::configuration_exception(
|
||||
fmt::sprintf("%s/%s must be a power of 2.", CHUNK_LENGTH_KB, CHUNK_LENGTH_KB_ERR));
|
||||
throw exceptions::configuration_exception(sstring(CHUNK_LENGTH_KB) + " must be a power of 2.");
|
||||
}
|
||||
}
|
||||
if (_crc_check_chance && (_crc_check_chance.value() < 0.0 || _crc_check_chance.value() > 1.0)) {
|
||||
@@ -197,7 +192,6 @@ void compression_parameters::validate_options(const std::map<sstring, sstring>&
|
||||
static std::set<sstring> keywords({
|
||||
sstring(SSTABLE_COMPRESSION),
|
||||
sstring(CHUNK_LENGTH_KB),
|
||||
sstring(CHUNK_LENGTH_KB_ERR),
|
||||
sstring(CRC_CHECK_CHANCE),
|
||||
});
|
||||
std::set<sstring> ckw;
|
||||
@@ -206,7 +200,7 @@ void compression_parameters::validate_options(const std::map<sstring, sstring>&
|
||||
}
|
||||
for (auto&& opt : options) {
|
||||
if (!keywords.count(opt.first) && !ckw.count(opt.first)) {
|
||||
throw exceptions::configuration_exception(format("Unknown compression option '{}'.", opt.first));
|
||||
throw exceptions::configuration_exception(sprint("Unknown compression option '%s'.", opt.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
compress.hh
12
compress.hh
@@ -29,6 +29,7 @@
|
||||
#include <seastar/core/sstring.hh>
|
||||
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "stdx.hh"
|
||||
|
||||
|
||||
class compressor {
|
||||
@@ -72,7 +73,7 @@ public:
|
||||
}
|
||||
|
||||
// to cheaply bridge sstable compression options / maps
|
||||
using opt_string = std::optional<sstring>;
|
||||
using opt_string = stdx::optional<sstring>;
|
||||
using opt_getter = std::function<opt_string(const sstring&)>;
|
||||
|
||||
static shared_ptr<compressor> create(const sstring& name, const opt_getter&);
|
||||
@@ -98,12 +99,11 @@ public:
|
||||
|
||||
static const sstring SSTABLE_COMPRESSION;
|
||||
static const sstring CHUNK_LENGTH_KB;
|
||||
static const sstring CHUNK_LENGTH_KB_ERR;
|
||||
static const sstring CRC_CHECK_CHANCE;
|
||||
private:
|
||||
compressor_ptr _compressor;
|
||||
std::optional<int> _chunk_length;
|
||||
std::optional<double> _crc_check_chance;
|
||||
std::experimental::optional<int> _chunk_length;
|
||||
std::experimental::optional<double> _crc_check_chance;
|
||||
public:
|
||||
compression_parameters();
|
||||
compression_parameters(compressor_ptr);
|
||||
@@ -118,10 +118,6 @@ public:
|
||||
std::map<sstring, sstring> get_options() const;
|
||||
bool operator==(const compression_parameters& other) const;
|
||||
bool operator!=(const compression_parameters& other) const;
|
||||
|
||||
static compression_parameters no_compression() {
|
||||
return compression_parameters(nullptr);
|
||||
}
|
||||
private:
|
||||
void validate_options(const std::map<sstring, sstring>&);
|
||||
};
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
# of tokens assuming they have equal hardware capability.
|
||||
#
|
||||
# If you already have a cluster with 1 token per node, and wish to migrate to
|
||||
# multiple tokens per node, see http://cassandra.apache.org/doc/latest/operating
|
||||
# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations
|
||||
num_tokens: 256
|
||||
|
||||
# Directory where Scylla should store data on disk.
|
||||
# If not set, the default directory is /var/lib/scylla/data.
|
||||
# If not set, the default directory is $CASSANDRA_HOME/data/data.
|
||||
data_file_directories:
|
||||
- /var/lib/scylla/data
|
||||
|
||||
# commit log. when running on magnetic HDD, this should be a
|
||||
# separate spindle than the data directories.
|
||||
# If not set, the default directory is /var/lib/scylla/commitlog.
|
||||
# If not set, the default directory is $CASSANDRA_HOME/data/commitlog.
|
||||
commitlog_directory: /var/lib/scylla/commitlog
|
||||
|
||||
# commitlog_sync may be either "periodic" or "batch."
|
||||
@@ -65,7 +65,7 @@ commitlog_sync_period_in_ms: 10000
|
||||
commitlog_segment_size_in_mb: 32
|
||||
|
||||
# seed_provider class_name is saved for future use.
|
||||
# seeds address(es) are mandatory!
|
||||
# seeds address are mandatory!
|
||||
seed_provider:
|
||||
# Addresses of hosts that are deemed contact points.
|
||||
# Scylla nodes use this list of hosts to find each other and learn
|
||||
@@ -242,11 +242,8 @@ batch_size_fail_threshold_in_kb: 50
|
||||
|
||||
# The directory where hints files are stored if hinted handoff is enabled.
|
||||
# hints_directory: /var/lib/scylla/hints
|
||||
|
||||
# The directory where hints files are stored for materialized-view updates
|
||||
# view_hints_directory: /var/lib/scylla/view_hints
|
||||
|
||||
# See https://docs.scylladb.com/architecture/anti-entropy/hinted-handoff
|
||||
# See http://wiki.apache.org/cassandra/HintedHandoff
|
||||
# May either be "true" or "false" to enable globally, or contain a list
|
||||
# of data centers to enable per-datacenter.
|
||||
# hinted_handoff_enabled: DC1,DC2
|
||||
@@ -392,7 +389,7 @@ partitioner: org.apache.cassandra.dht.Murmur3Partitioner
|
||||
# memory_allocator: NativeAllocator
|
||||
|
||||
# saved caches
|
||||
# If not set, the default directory is /var/lib/scylla/saved_caches.
|
||||
# If not set, the default directory is $CASSANDRA_HOME/data/saved_caches.
|
||||
# saved_caches_directory: /var/lib/scylla/saved_caches
|
||||
|
||||
|
||||
@@ -538,7 +535,7 @@ commitlog_total_space_in_mb: -1
|
||||
# /proc/sys/net/core/wmem_max
|
||||
# /proc/sys/net/core/rmem_max
|
||||
# /proc/sys/net/ipv4/tcp_wmem
|
||||
# /proc/sys/net/ipv4/tcp_rmem
|
||||
# /proc/sys/net/ipv4/tcp_wmem
|
||||
# and: man tcp
|
||||
# internode_send_buff_size_in_bytes:
|
||||
# internode_recv_buff_size_in_bytes:
|
||||
@@ -612,12 +609,9 @@ commitlog_total_space_in_mb: -1
|
||||
# of compaction, including validation compaction.
|
||||
# compaction_throughput_mb_per_sec: 16
|
||||
|
||||
# Log a warning when writing partitions larger than this value
|
||||
# Log a warning when compacting partitions larger than this value
|
||||
# compaction_large_partition_warning_threshold_mb: 100
|
||||
|
||||
# Log a warning when writing rows larger than this value
|
||||
# compaction_large_row_warning_threshold_mb: 50
|
||||
|
||||
# When compacting, the replacement sstable(s) can be opened before they
|
||||
# are completely written, and used in place of the prior sstables for
|
||||
# any range that has been written. This helps to smoothly transfer reads
|
||||
|
||||
1104
configure.py
1104
configure.py
File diff suppressed because it is too large
Load Diff
@@ -38,45 +38,28 @@ private:
|
||||
static bool is_compatible(const column_definition& new_def, const data_type& old_type, column_kind kind) {
|
||||
return ::is_compatible(new_def.kind, kind) && new_def.type->is_value_compatible_with(*old_type);
|
||||
}
|
||||
static atomic_cell upgrade_cell(const abstract_type& new_type, const abstract_type& old_type, atomic_cell_view cell,
|
||||
atomic_cell::collection_member cm = atomic_cell::collection_member::no) {
|
||||
if (cell.is_live() && !old_type.is_counter()) {
|
||||
if (cell.is_live_and_has_ttl()) {
|
||||
return atomic_cell::make_live(new_type, cell.timestamp(), cell.value().linearize(), cell.expiry(), cell.ttl(), cm);
|
||||
}
|
||||
return atomic_cell::make_live(new_type, cell.timestamp(), cell.value().linearize(), cm);
|
||||
} else {
|
||||
return atomic_cell(new_type, cell);
|
||||
}
|
||||
}
|
||||
static void accept_cell(row& dst, column_kind kind, const column_definition& new_def, const data_type& old_type, atomic_cell_view cell) {
|
||||
if (!is_compatible(new_def, old_type, kind) || cell.timestamp() <= new_def.dropped_at()) {
|
||||
return;
|
||||
if (is_compatible(new_def, old_type, kind) && cell.timestamp() > new_def.dropped_at()) {
|
||||
dst.apply(new_def, atomic_cell_or_collection(cell));
|
||||
}
|
||||
dst.apply(new_def, upgrade_cell(*new_def.type, *old_type, cell));
|
||||
}
|
||||
static void accept_cell(row& dst, column_kind kind, const column_definition& new_def, const data_type& old_type, collection_mutation_view cell) {
|
||||
if (!is_compatible(new_def, old_type, kind)) {
|
||||
return;
|
||||
}
|
||||
cell.data.with_linearized([&] (bytes_view cell_bv) {
|
||||
auto new_ctype = static_pointer_cast<const collection_type_impl>(new_def.type);
|
||||
auto old_ctype = static_pointer_cast<const collection_type_impl>(old_type);
|
||||
auto old_view = old_ctype->deserialize_mutation_form(cell_bv);
|
||||
auto&& ctype = static_pointer_cast<const collection_type_impl>(old_type);
|
||||
auto old_view = ctype->deserialize_mutation_form(cell);
|
||||
|
||||
collection_type_impl::mutation new_view;
|
||||
collection_type_impl::mutation_view new_view;
|
||||
if (old_view.tomb.timestamp > new_def.dropped_at()) {
|
||||
new_view.tomb = old_view.tomb;
|
||||
}
|
||||
for (auto& c : old_view.cells) {
|
||||
if (c.second.timestamp() > new_def.dropped_at()) {
|
||||
new_view.cells.emplace_back(c.first, upgrade_cell(*new_ctype->value_comparator(), *old_ctype->value_comparator(), c.second, atomic_cell::collection_member::yes));
|
||||
new_view.cells.emplace_back(std::move(c));
|
||||
}
|
||||
}
|
||||
if (new_view.tomb || !new_view.cells.empty()) {
|
||||
dst.apply(new_def, new_ctype->serialize_mutation_form(std::move(new_view)));
|
||||
}
|
||||
});
|
||||
dst.apply(new_def, ctype->serialize_mutation_form(std::move(new_view)));
|
||||
}
|
||||
public:
|
||||
converting_mutation_partition_applier(
|
||||
@@ -92,10 +75,6 @@ public:
|
||||
_p.apply(t);
|
||||
}
|
||||
|
||||
void accept_static_cell(column_id id, atomic_cell cell) {
|
||||
return accept_static_cell(id, atomic_cell_view(cell));
|
||||
}
|
||||
|
||||
virtual void accept_static_cell(column_id id, atomic_cell_view cell) override {
|
||||
const column_mapping_entry& col = _visited_column_mapping.static_column_at(id);
|
||||
const column_definition* def = _p_schema.get_column_definition(col.name());
|
||||
@@ -123,10 +102,6 @@ public:
|
||||
_current_row = &r;
|
||||
}
|
||||
|
||||
void accept_row_cell(column_id id, atomic_cell cell) {
|
||||
return accept_row_cell(id, atomic_cell_view(cell));
|
||||
}
|
||||
|
||||
virtual void accept_row_cell(column_id id, atomic_cell_view cell) override {
|
||||
const column_mapping_entry& col = _visited_column_mapping.regular_column_at(id);
|
||||
const column_definition* def = _p_schema.get_column_definition(col.name());
|
||||
@@ -145,11 +120,11 @@ public:
|
||||
|
||||
// Appends the cell to dst upgrading it to the new schema.
|
||||
// Cells must have monotonic names.
|
||||
static void append_cell(row& dst, column_kind kind, const column_definition& new_def, const column_definition& old_def, const atomic_cell_or_collection& cell) {
|
||||
static void append_cell(row& dst, column_kind kind, const column_definition& new_def, const data_type& old_type, const atomic_cell_or_collection& cell) {
|
||||
if (new_def.is_atomic()) {
|
||||
accept_cell(dst, kind, new_def, old_def.type, cell.as_atomic_cell(old_def));
|
||||
accept_cell(dst, kind, new_def, old_type, cell.as_atomic_cell());
|
||||
} else {
|
||||
accept_cell(dst, kind, new_def, old_def.type, cell.as_collection_mutation());
|
||||
accept_cell(dst, kind, new_def, old_type, cell.as_collection_mutation());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
126
counters.cc
126
counters.cc
@@ -78,10 +78,10 @@ std::vector<counter_shard> counter_cell_view::shards_compatible_with_1_7_4() con
|
||||
return sorted_shards;
|
||||
}
|
||||
|
||||
static bool apply_in_place(const column_definition& cdef, atomic_cell_mutable_view dst, atomic_cell_mutable_view src)
|
||||
static bool apply_in_place(atomic_cell_or_collection& dst, atomic_cell_or_collection& src)
|
||||
{
|
||||
auto dst_ccmv = counter_cell_mutable_view(dst);
|
||||
auto src_ccmv = counter_cell_mutable_view(src);
|
||||
auto dst_ccmv = counter_cell_mutable_view(dst.as_mutable_atomic_cell());
|
||||
auto src_ccmv = counter_cell_mutable_view(src.as_mutable_atomic_cell());
|
||||
auto dst_shards = dst_ccmv.shards();
|
||||
auto src_shards = src_ccmv.shards();
|
||||
|
||||
@@ -118,19 +118,48 @@ static bool apply_in_place(const column_definition& cdef, atomic_cell_mutable_vi
|
||||
auto src_ts = src_ccmv.timestamp();
|
||||
dst_ccmv.set_timestamp(std::max(dst_ts, src_ts));
|
||||
src_ccmv.set_timestamp(dst_ts);
|
||||
src.as_mutable_atomic_cell().set_counter_in_place_revert(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void counter_cell_view::apply(const column_definition& cdef, atomic_cell_or_collection& dst, atomic_cell_or_collection& src)
|
||||
static void revert_in_place_apply(atomic_cell_or_collection& dst, atomic_cell_or_collection& src)
|
||||
{
|
||||
auto dst_ac = dst.as_atomic_cell(cdef);
|
||||
auto src_ac = src.as_atomic_cell(cdef);
|
||||
assert(dst.can_use_mutable_view() && src.can_use_mutable_view());
|
||||
auto dst_ccmv = counter_cell_mutable_view(dst.as_mutable_atomic_cell());
|
||||
auto src_ccmv = counter_cell_mutable_view(src.as_mutable_atomic_cell());
|
||||
auto dst_shards = dst_ccmv.shards();
|
||||
auto src_shards = src_ccmv.shards();
|
||||
|
||||
auto dst_it = dst_shards.begin();
|
||||
auto src_it = src_shards.begin();
|
||||
|
||||
while (src_it != src_shards.end()) {
|
||||
while (dst_it != dst_shards.end() && dst_it->id() < src_it->id()) {
|
||||
++dst_it;
|
||||
}
|
||||
assert(dst_it != dst_shards.end() && dst_it->id() == src_it->id());
|
||||
dst_it->swap_value_and_clock(*src_it);
|
||||
++src_it;
|
||||
}
|
||||
|
||||
auto dst_ts = dst_ccmv.timestamp();
|
||||
auto src_ts = src_ccmv.timestamp();
|
||||
dst_ccmv.set_timestamp(src_ts);
|
||||
src_ccmv.set_timestamp(dst_ts);
|
||||
src.as_mutable_atomic_cell().set_counter_in_place_revert(false);
|
||||
}
|
||||
|
||||
bool counter_cell_view::apply_reversibly(atomic_cell_or_collection& dst, atomic_cell_or_collection& src)
|
||||
{
|
||||
auto dst_ac = dst.as_atomic_cell();
|
||||
auto src_ac = src.as_atomic_cell();
|
||||
|
||||
if (!dst_ac.is_live() || !src_ac.is_live()) {
|
||||
if (dst_ac.is_live() || (!src_ac.is_live() && compare_atomic_cell_for_merge(dst_ac, src_ac) < 0)) {
|
||||
std::swap(dst, src);
|
||||
return true;
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dst_ac.is_counter_update() && src_ac.is_counter_update()) {
|
||||
@@ -138,26 +167,22 @@ void counter_cell_view::apply(const column_definition& cdef, atomic_cell_or_coll
|
||||
auto dst_v = dst_ac.counter_update_value();
|
||||
dst = atomic_cell::make_live_counter_update(std::max(dst_ac.timestamp(), src_ac.timestamp()),
|
||||
src_v + dst_v);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(!dst_ac.is_counter_update());
|
||||
assert(!src_ac.is_counter_update());
|
||||
with_linearized(dst_ac, [&] (counter_cell_view dst_ccv) {
|
||||
with_linearized(src_ac, [&] (counter_cell_view src_ccv) {
|
||||
|
||||
if (dst_ccv.shard_count() >= src_ccv.shard_count()) {
|
||||
auto dst_amc = dst.as_mutable_atomic_cell(cdef);
|
||||
auto src_amc = src.as_mutable_atomic_cell(cdef);
|
||||
if (!dst_amc.is_value_fragmented() && !src_amc.is_value_fragmented()) {
|
||||
if (apply_in_place(cdef, dst_amc, src_amc)) {
|
||||
return;
|
||||
}
|
||||
if (counter_cell_view(dst_ac).shard_count() >= counter_cell_view(src_ac).shard_count()
|
||||
&& dst.can_use_mutable_view() && src.can_use_mutable_view()) {
|
||||
if (apply_in_place(dst, src)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
auto dst_shards = dst_ccv.shards();
|
||||
auto src_shards = src_ccv.shards();
|
||||
src.as_mutable_atomic_cell().set_counter_in_place_revert(false);
|
||||
auto dst_shards = counter_cell_view(dst_ac).shards();
|
||||
auto src_shards = counter_cell_view(src_ac).shards();
|
||||
|
||||
counter_cell_builder result;
|
||||
combine(dst_shards.begin(), dst_shards.end(), src_shards.begin(), src_shards.end(),
|
||||
@@ -166,27 +191,38 @@ void counter_cell_view::apply(const column_definition& cdef, atomic_cell_or_coll
|
||||
});
|
||||
|
||||
auto cell = result.build(std::max(dst_ac.timestamp(), src_ac.timestamp()));
|
||||
src = std::exchange(dst, atomic_cell_or_collection(std::move(cell)));
|
||||
});
|
||||
});
|
||||
src = std::exchange(dst, atomic_cell_or_collection(cell));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<atomic_cell> counter_cell_view::difference(atomic_cell_view a, atomic_cell_view b)
|
||||
void counter_cell_view::revert_apply(atomic_cell_or_collection& dst, atomic_cell_or_collection& src)
|
||||
{
|
||||
if (dst.as_atomic_cell().is_counter_update()) {
|
||||
auto src_v = src.as_atomic_cell().counter_update_value();
|
||||
auto dst_v = dst.as_atomic_cell().counter_update_value();
|
||||
dst = atomic_cell::make_live(dst.as_atomic_cell().timestamp(),
|
||||
long_type->decompose(dst_v - src_v));
|
||||
} else if (src.as_atomic_cell().is_counter_in_place_revert_set()) {
|
||||
revert_in_place_apply(dst, src);
|
||||
} else {
|
||||
std::swap(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
stdx::optional<atomic_cell> counter_cell_view::difference(atomic_cell_view a, atomic_cell_view b)
|
||||
{
|
||||
assert(!a.is_counter_update());
|
||||
assert(!b.is_counter_update());
|
||||
|
||||
if (!b.is_live() || !a.is_live()) {
|
||||
if (b.is_live() || (!a.is_live() && compare_atomic_cell_for_merge(b, a) < 0)) {
|
||||
return atomic_cell(*counter_type, a);
|
||||
return atomic_cell(a);
|
||||
}
|
||||
return { };
|
||||
}
|
||||
|
||||
return with_linearized(a, [&] (counter_cell_view a_ccv) {
|
||||
return with_linearized(b, [&] (counter_cell_view b_ccv) {
|
||||
auto a_shards = a_ccv.shards();
|
||||
auto b_shards = b_ccv.shards();
|
||||
auto a_shards = counter_cell_view(a).shards();
|
||||
auto b_shards = counter_cell_view(b).shards();
|
||||
|
||||
auto a_it = a_shards.begin();
|
||||
auto a_end = a_shards.end();
|
||||
@@ -204,25 +240,22 @@ std::optional<atomic_cell> counter_cell_view::difference(atomic_cell_view a, ato
|
||||
++a_it;
|
||||
}
|
||||
|
||||
std::optional<atomic_cell> diff;
|
||||
stdx::optional<atomic_cell> diff;
|
||||
if (!result.empty()) {
|
||||
diff = result.build(std::max(a.timestamp(), b.timestamp()));
|
||||
} else if (a.timestamp() > b.timestamp()) {
|
||||
diff = atomic_cell::make_live(*counter_type, a.timestamp(), bytes_view());
|
||||
diff = atomic_cell::make_live(a.timestamp(), bytes_view());
|
||||
}
|
||||
return diff;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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] (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);
|
||||
auto transform_new_row_to_shards = [clock_offset] (auto& cells) {
|
||||
cells.for_each_cell([clock_offset] (auto, atomic_cell_or_collection& ac_o_c) {
|
||||
auto acv = ac_o_c.as_atomic_cell();
|
||||
if (!acv.is_live()) {
|
||||
return; // continue -- we are in lambda
|
||||
}
|
||||
@@ -233,35 +266,32 @@ void transform_counter_updates_to_shards(mutation& m, const mutation* current_st
|
||||
};
|
||||
|
||||
if (!current_state) {
|
||||
transform_new_row_to_shards(column_kind::static_column, m.partition().static_row());
|
||||
transform_new_row_to_shards(m.partition().static_row());
|
||||
for (auto& cr : m.partition().clustered_rows()) {
|
||||
transform_new_row_to_shards(column_kind::regular_column, cr.row().cells());
|
||||
transform_new_row_to_shards(cr.row().cells());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
clustering_key::less_compare cmp(*m.schema());
|
||||
|
||||
auto transform_row_to_shards = [&s = *m.schema(), clock_offset] (column_kind kind, auto& transformee, auto& state) {
|
||||
auto transform_row_to_shards = [clock_offset] (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);
|
||||
auto acv = ac_o_c.as_atomic_cell(cdef);
|
||||
auto acv = ac_o_c.as_atomic_cell();
|
||||
if (!acv.is_live()) {
|
||||
return; // continue -- we are in lambda
|
||||
}
|
||||
counter_cell_view::with_linearized(acv, [&] (counter_cell_view ccv) {
|
||||
counter_cell_view ccv(acv);
|
||||
auto cs = ccv.local_shard();
|
||||
if (!cs) {
|
||||
return; // continue
|
||||
}
|
||||
shards.emplace_back(std::make_pair(id, counter_shard(*cs)));
|
||||
});
|
||||
});
|
||||
|
||||
transformee.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);
|
||||
auto acv = ac_o_c.as_atomic_cell();
|
||||
if (!acv.is_live()) {
|
||||
return; // continue -- we are in lambda
|
||||
}
|
||||
@@ -283,7 +313,7 @@ void transform_counter_updates_to_shards(mutation& m, const mutation* current_st
|
||||
});
|
||||
};
|
||||
|
||||
transform_row_to_shards(column_kind::static_column, m.partition().static_row(), current_state->partition().static_row());
|
||||
transform_row_to_shards(m.partition().static_row(), current_state->partition().static_row());
|
||||
|
||||
auto& cstate = current_state->partition();
|
||||
auto it = cstate.clustered_rows().begin();
|
||||
@@ -293,10 +323,10 @@ void transform_counter_updates_to_shards(mutation& m, const mutation* current_st
|
||||
++it;
|
||||
}
|
||||
if (it == end || cmp(cr.key(), it->key())) {
|
||||
transform_new_row_to_shards(column_kind::regular_column, cr.row().cells());
|
||||
transform_new_row_to_shards(cr.row().cells());
|
||||
continue;
|
||||
}
|
||||
|
||||
transform_row_to_shards(column_kind::regular_column, cr.row().cells(), it->row().cells());
|
||||
transform_row_to_shards(cr.row().cells(), it->row().cells());
|
||||
}
|
||||
}
|
||||
|
||||
114
counters.hh
114
counters.hh
@@ -26,6 +26,8 @@
|
||||
#include "atomic_cell_or_collection.hh"
|
||||
#include "types.hh"
|
||||
|
||||
#include "stdx.hh"
|
||||
|
||||
class mutation;
|
||||
|
||||
class mutation;
|
||||
@@ -77,7 +79,7 @@ static_assert(std::is_pod<counter_id>::value, "counter_id should be a POD type")
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const counter_id& id);
|
||||
|
||||
template<mutable_view is_mutable>
|
||||
template<typename View>
|
||||
class basic_counter_shard_view {
|
||||
enum class offset : unsigned {
|
||||
id = 0u,
|
||||
@@ -86,8 +88,7 @@ class basic_counter_shard_view {
|
||||
total_size = unsigned(logical_clock) + sizeof(int64_t),
|
||||
};
|
||||
private:
|
||||
using pointer_type = std::conditional_t<is_mutable == mutable_view::no, const signed char*, signed char*>;
|
||||
pointer_type _base;
|
||||
typename View::pointer _base;
|
||||
private:
|
||||
template<typename T>
|
||||
T read(offset off) const {
|
||||
@@ -99,7 +100,7 @@ public:
|
||||
static constexpr auto size = size_t(offset::total_size);
|
||||
public:
|
||||
basic_counter_shard_view() = default;
|
||||
explicit basic_counter_shard_view(pointer_type ptr) noexcept
|
||||
explicit basic_counter_shard_view(typename View::pointer ptr) noexcept
|
||||
: _base(ptr) { }
|
||||
|
||||
counter_id id() const { return read<counter_id>(offset::id); }
|
||||
@@ -110,7 +111,7 @@ public:
|
||||
static constexpr size_t off = size_t(offset::value);
|
||||
static constexpr size_t size = size_t(offset::total_size) - off;
|
||||
|
||||
signed char tmp[size];
|
||||
typename View::value_type tmp[size];
|
||||
std::copy_n(_base + off, size, tmp);
|
||||
std::copy_n(other._base + off, size, _base + off);
|
||||
std::copy_n(tmp, size, other._base + off);
|
||||
@@ -137,7 +138,7 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
using counter_shard_view = basic_counter_shard_view<mutable_view::no>;
|
||||
using counter_shard_view = basic_counter_shard_view<bytes_view>;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, counter_shard_view csv);
|
||||
|
||||
@@ -197,7 +198,7 @@ public:
|
||||
return do_apply(other);
|
||||
}
|
||||
|
||||
static constexpr size_t serialized_size() {
|
||||
static size_t serialized_size() {
|
||||
return counter_shard_view::size;
|
||||
}
|
||||
void serialize(bytes::iterator& out) const {
|
||||
@@ -251,33 +252,15 @@ public:
|
||||
}
|
||||
|
||||
atomic_cell build(api::timestamp_type timestamp) const {
|
||||
// If we can assume that the counter shards never cross fragment boundaries
|
||||
// the serialisation code gets much simpler.
|
||||
static_assert(data::cell::maximum_external_chunk_length % counter_shard::serialized_size() == 0);
|
||||
|
||||
auto ac = atomic_cell::make_live_uninitialized(*counter_type, timestamp, serialized_size());
|
||||
|
||||
auto dst_it = ac.value().begin();
|
||||
auto dst_current = *dst_it++;
|
||||
for (auto&& cs : _shards) {
|
||||
if (dst_current.empty()) {
|
||||
dst_current = *dst_it++;
|
||||
}
|
||||
assert(!dst_current.empty());
|
||||
auto value_dst = dst_current.data();
|
||||
cs.serialize(value_dst);
|
||||
dst_current.remove_prefix(counter_shard::serialized_size());
|
||||
}
|
||||
return ac;
|
||||
return atomic_cell::make_live_from_serializer(timestamp, serialized_size(), [this] (bytes::iterator out) {
|
||||
serialize(out);
|
||||
});
|
||||
}
|
||||
|
||||
static atomic_cell from_single_shard(api::timestamp_type timestamp, const counter_shard& cs) {
|
||||
// We don't really need to bother with fragmentation here.
|
||||
static_assert(data::cell::maximum_external_chunk_length >= counter_shard::serialized_size());
|
||||
auto ac = atomic_cell::make_live_uninitialized(*counter_type, timestamp, counter_shard::serialized_size());
|
||||
auto dst = ac.value().first_fragment().begin();
|
||||
cs.serialize(dst);
|
||||
return ac;
|
||||
return atomic_cell::make_live_from_serializer(timestamp, counter_shard::serialized_size(), [&cs] (bytes::iterator out) {
|
||||
cs.serialize(out);
|
||||
});
|
||||
}
|
||||
|
||||
class inserter_iterator : public std::iterator<std::output_iterator_tag, counter_shard> {
|
||||
@@ -304,32 +287,28 @@ public:
|
||||
// <counter_id> := <int64_t><int64_t>
|
||||
// <shard> := <counter_id><int64_t:value><int64_t:logical_clock>
|
||||
// <counter_cell> := <shard>*
|
||||
template<mutable_view is_mutable>
|
||||
template<typename View>
|
||||
class basic_counter_cell_view {
|
||||
protected:
|
||||
using linearized_value_view = std::conditional_t<is_mutable == mutable_view::no,
|
||||
bytes_view, bytes_mutable_view>;
|
||||
using pointer_type = typename linearized_value_view::pointer;
|
||||
basic_atomic_cell_view<is_mutable> _cell;
|
||||
linearized_value_view _value;
|
||||
atomic_cell_base<View> _cell;
|
||||
private:
|
||||
class shard_iterator : public std::iterator<std::input_iterator_tag, basic_counter_shard_view<is_mutable>> {
|
||||
pointer_type _current;
|
||||
basic_counter_shard_view<is_mutable> _current_view;
|
||||
class shard_iterator : public std::iterator<std::input_iterator_tag, basic_counter_shard_view<View>> {
|
||||
typename View::pointer _current;
|
||||
basic_counter_shard_view<View> _current_view;
|
||||
public:
|
||||
shard_iterator() = default;
|
||||
shard_iterator(pointer_type ptr) noexcept
|
||||
shard_iterator(typename View::pointer ptr) noexcept
|
||||
: _current(ptr), _current_view(ptr) { }
|
||||
|
||||
basic_counter_shard_view<is_mutable>& operator*() noexcept {
|
||||
basic_counter_shard_view<View>& operator*() noexcept {
|
||||
return _current_view;
|
||||
}
|
||||
basic_counter_shard_view<is_mutable>* operator->() noexcept {
|
||||
basic_counter_shard_view<View>* operator->() noexcept {
|
||||
return &_current_view;
|
||||
}
|
||||
shard_iterator& operator++() noexcept {
|
||||
_current += counter_shard_view::size;
|
||||
_current_view = basic_counter_shard_view<is_mutable>(_current);
|
||||
_current_view = basic_counter_shard_view<View>(_current);
|
||||
return *this;
|
||||
}
|
||||
shard_iterator operator++(int) noexcept {
|
||||
@@ -339,7 +318,7 @@ private:
|
||||
}
|
||||
shard_iterator& operator--() noexcept {
|
||||
_current -= counter_shard_view::size;
|
||||
_current_view = basic_counter_shard_view<is_mutable>(_current);
|
||||
_current_view = basic_counter_shard_view<View>(_current);
|
||||
return *this;
|
||||
}
|
||||
shard_iterator operator--(int) noexcept {
|
||||
@@ -356,23 +335,22 @@ private:
|
||||
};
|
||||
public:
|
||||
boost::iterator_range<shard_iterator> shards() const {
|
||||
auto begin = shard_iterator(_value.data());
|
||||
auto end = shard_iterator(_value.data() + _value.size());
|
||||
auto bv = _cell.value();
|
||||
auto begin = shard_iterator(bv.data());
|
||||
auto end = shard_iterator(bv.data() + bv.size());
|
||||
return boost::make_iterator_range(begin, end);
|
||||
}
|
||||
|
||||
size_t shard_count() const {
|
||||
return _cell.value().size_bytes() / counter_shard_view::size;
|
||||
return _cell.value().size() / counter_shard_view::size;
|
||||
}
|
||||
protected:
|
||||
public:
|
||||
// ac must be a live counter cell
|
||||
explicit basic_counter_cell_view(basic_atomic_cell_view<is_mutable> ac, linearized_value_view vv) noexcept
|
||||
: _cell(ac), _value(vv)
|
||||
{
|
||||
explicit basic_counter_cell_view(atomic_cell_base<View> ac) noexcept : _cell(ac) {
|
||||
assert(_cell.is_live());
|
||||
assert(!_cell.is_counter_update());
|
||||
}
|
||||
public:
|
||||
|
||||
api::timestamp_type timestamp() const { return _cell.timestamp(); }
|
||||
|
||||
static data_type total_value_type() { return long_type; }
|
||||
@@ -383,7 +361,7 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<counter_shard_view> get_shard(const counter_id& id) const {
|
||||
stdx::optional<counter_shard_view> get_shard(const counter_id& id) const {
|
||||
auto it = boost::range::find_if(shards(), [&id] (counter_shard_view csv) {
|
||||
return csv.id() == id;
|
||||
});
|
||||
@@ -393,7 +371,7 @@ public:
|
||||
return *it;
|
||||
}
|
||||
|
||||
std::optional<counter_shard_view> local_shard() const {
|
||||
stdx::optional<counter_shard_view> local_shard() const {
|
||||
// TODO: consider caching local shard position
|
||||
return get_shard(counter_id::local());
|
||||
}
|
||||
@@ -403,39 +381,29 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct counter_cell_view : basic_counter_cell_view<mutable_view::no> {
|
||||
struct counter_cell_view : basic_counter_cell_view<bytes_view> {
|
||||
using basic_counter_cell_view::basic_counter_cell_view;
|
||||
|
||||
template<typename Function>
|
||||
static decltype(auto) with_linearized(basic_atomic_cell_view<mutable_view::no> ac, Function&& fn) {
|
||||
return ac.value().with_linearized([&] (bytes_view value_view) {
|
||||
counter_cell_view ccv(ac, value_view);
|
||||
return fn(ccv);
|
||||
});
|
||||
}
|
||||
|
||||
// Returns counter shards in an order that is compatible with Scylla 1.7.4.
|
||||
std::vector<counter_shard> shards_compatible_with_1_7_4() const;
|
||||
|
||||
// Reversibly applies two counter cells, at least one of them must be live.
|
||||
static void apply(const column_definition& cdef, atomic_cell_or_collection& dst, atomic_cell_or_collection& src);
|
||||
// Returns true iff dst was modified.
|
||||
static bool apply_reversibly(atomic_cell_or_collection& dst, atomic_cell_or_collection& src);
|
||||
|
||||
// Reverts apply performed by apply_reversible().
|
||||
static void revert_apply(atomic_cell_or_collection& dst, atomic_cell_or_collection& src);
|
||||
|
||||
// Computes a counter cell containing minimal amount of data which, when
|
||||
// applied to 'b' returns the same cell as 'a' and 'b' applied together.
|
||||
static std::optional<atomic_cell> difference(atomic_cell_view a, atomic_cell_view b);
|
||||
static stdx::optional<atomic_cell> difference(atomic_cell_view a, atomic_cell_view b);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, counter_cell_view ccv);
|
||||
};
|
||||
|
||||
struct counter_cell_mutable_view : basic_counter_cell_view<mutable_view::yes> {
|
||||
struct counter_cell_mutable_view : basic_counter_cell_view<bytes_mutable_view> {
|
||||
using basic_counter_cell_view::basic_counter_cell_view;
|
||||
|
||||
explicit counter_cell_mutable_view(atomic_cell_mutable_view ac) noexcept
|
||||
: basic_counter_cell_view<mutable_view::yes>(ac, ac.value().first_fragment())
|
||||
{
|
||||
assert(!ac.value().is_fragmented());
|
||||
}
|
||||
|
||||
void set_timestamp(api::timestamp_type ts) { _cell.set_timestamp(ts); }
|
||||
};
|
||||
|
||||
|
||||
223
cql3/Cql.g
223
cql3/Cql.g
@@ -91,7 +91,7 @@ options {
|
||||
#include "cql3/ut_name.hh"
|
||||
#include "cql3/functions/function_name.hh"
|
||||
#include "cql3/functions/function_call.hh"
|
||||
#include <seastar/core/sstring.hh>
|
||||
#include "core/sstring.hh"
|
||||
#include "CqlLexer.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -115,7 +115,7 @@ using operations_type = std::vector<std::pair<::shared_ptr<cql3::column_identifi
|
||||
// problem. It is up to the user to ensure it is actually assigned to.
|
||||
template <typename T>
|
||||
struct uninitialized {
|
||||
std::optional<T> _val;
|
||||
std::experimental::optional<T> _val;
|
||||
uninitialized() = default;
|
||||
uninitialized(const uninitialized&) = default;
|
||||
uninitialized(uninitialized&&) = default;
|
||||
@@ -242,7 +242,7 @@ struct uninitialized {
|
||||
return res;
|
||||
}
|
||||
|
||||
bool convert_boolean_literal(std::string_view s) {
|
||||
bool convert_boolean_literal(stdx::string_view s) {
|
||||
std::string lower_s(s.size(), '\0');
|
||||
std::transform(s.cbegin(), s.cend(), lower_s.begin(), &::tolower);
|
||||
return lower_s == "true";
|
||||
@@ -253,7 +253,8 @@ struct uninitialized {
|
||||
{
|
||||
for (auto&& p : operations) {
|
||||
if (*p.first == *key && !p.second->is_compatible_with(update)) {
|
||||
add_recognition_error(format("Multiple incompatible setting of column {}", *key));
|
||||
// \%s is escaped for antlr
|
||||
add_recognition_error(sprint("Multiple incompatible setting of column \%s", *key));
|
||||
}
|
||||
}
|
||||
operations.emplace_back(std::move(key), std::move(update));
|
||||
@@ -372,7 +373,7 @@ useStatement returns [::shared_ptr<raw::use_statement> stmt]
|
||||
;
|
||||
|
||||
/**
|
||||
* SELECT [JSON] <expression>
|
||||
* SELECT <expression>
|
||||
* FROM <CF>
|
||||
* WHERE KEY = "key1" AND COL > 1 AND COL < 100
|
||||
* LIMIT <NUMBER>;
|
||||
@@ -381,28 +382,21 @@ selectStatement returns [shared_ptr<raw::select_statement> expr]
|
||||
@init {
|
||||
bool is_distinct = false;
|
||||
::shared_ptr<cql3::term::raw> limit;
|
||||
::shared_ptr<cql3::term::raw> per_partition_limit;
|
||||
raw::select_statement::parameters::orderings_type orderings;
|
||||
bool allow_filtering = false;
|
||||
bool is_json = false;
|
||||
bool bypass_cache = false;
|
||||
}
|
||||
: K_SELECT (
|
||||
( K_JSON { is_json = true; } )?
|
||||
( K_DISTINCT { is_distinct = true; } )?
|
||||
sclause=selectClause
|
||||
: K_SELECT ( ( K_DISTINCT { is_distinct = true; } )?
|
||||
sclause=selectClause
|
||||
)
|
||||
K_FROM cf=columnFamilyName
|
||||
( K_WHERE wclause=whereClause )?
|
||||
( K_ORDER K_BY orderByClause[orderings] ( ',' orderByClause[orderings] )* )?
|
||||
( K_PER K_PARTITION K_LIMIT rows=intValue { per_partition_limit = rows; } )?
|
||||
( K_LIMIT rows=intValue { limit = rows; } )?
|
||||
( K_ALLOW K_FILTERING { allow_filtering = true; } )?
|
||||
( K_BYPASS K_CACHE { bypass_cache = true; })?
|
||||
{
|
||||
auto params = ::make_shared<raw::select_statement::parameters>(std::move(orderings), is_distinct, allow_filtering, is_json, bypass_cache);
|
||||
auto params = ::make_shared<raw::select_statement::parameters>(std::move(orderings), is_distinct, allow_filtering);
|
||||
$expr = ::make_shared<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(sclause), std::move(wclause), std::move(limit));
|
||||
}
|
||||
;
|
||||
|
||||
@@ -454,54 +448,33 @@ orderByClause[raw::select_statement::parameters::orderings_type& orderings]
|
||||
: c=cident (K_ASC | K_DESC { reversed = true; })? { orderings.emplace_back(c, reversed); }
|
||||
;
|
||||
|
||||
jsonValue returns [::shared_ptr<cql3::term::raw> value]
|
||||
:
|
||||
| s=STRING_LITERAL { $value = cql3::constants::literal::string(sstring{$s.text}); }
|
||||
| ':' id=ident { $value = new_bind_variables(id); }
|
||||
| QMARK { $value = new_bind_variables(shared_ptr<cql3::column_identifier>{}); }
|
||||
;
|
||||
|
||||
/**
|
||||
* INSERT INTO <CF> (<column>, <column>, <column>, ...)
|
||||
* VALUES (<value>, <value>, <value>, ...)
|
||||
* USING TIMESTAMP <long>;
|
||||
*
|
||||
*/
|
||||
insertStatement returns [::shared_ptr<raw::modification_statement> expr]
|
||||
insertStatement returns [::shared_ptr<raw::insert_statement> expr]
|
||||
@init {
|
||||
auto attrs = ::make_shared<cql3::attributes::raw>();
|
||||
std::vector<::shared_ptr<cql3::column_identifier::raw>> column_names;
|
||||
std::vector<::shared_ptr<cql3::term::raw>> values;
|
||||
bool if_not_exists = false;
|
||||
bool default_unset = false;
|
||||
::shared_ptr<cql3::term::raw> json_value;
|
||||
}
|
||||
: K_INSERT K_INTO cf=columnFamilyName
|
||||
('(' c1=cident { column_names.push_back(c1); } ( ',' cn=cident { column_names.push_back(cn); } )* ')'
|
||||
K_VALUES
|
||||
'(' v1=term { values.push_back(v1); } ( ',' vn=term { values.push_back(vn); } )* ')'
|
||||
( K_IF K_NOT K_EXISTS { if_not_exists = true; } )?
|
||||
( usingClause[attrs] )?
|
||||
{
|
||||
$expr = ::make_shared<raw::insert_statement>(std::move(cf),
|
||||
std::move(attrs),
|
||||
std::move(column_names),
|
||||
std::move(values),
|
||||
if_not_exists);
|
||||
}
|
||||
| K_JSON
|
||||
json_token=jsonValue { json_value = $json_token.value; }
|
||||
( K_DEFAULT K_UNSET { default_unset = true; } | K_DEFAULT K_NULL )?
|
||||
( K_IF K_NOT K_EXISTS { if_not_exists = true; } )?
|
||||
( usingClause[attrs] )?
|
||||
{
|
||||
$expr = ::make_shared<raw::insert_json_statement>(std::move(cf),
|
||||
std::move(attrs),
|
||||
std::move(json_value),
|
||||
if_not_exists,
|
||||
default_unset);
|
||||
}
|
||||
)
|
||||
'(' c1=cident { column_names.push_back(c1); } ( ',' cn=cident { column_names.push_back(cn); } )* ')'
|
||||
K_VALUES
|
||||
'(' v1=term { values.push_back(v1); } ( ',' vn=term { values.push_back(vn); } )* ')'
|
||||
|
||||
( K_IF K_NOT K_EXISTS { if_not_exists = true; } )?
|
||||
( usingClause[attrs] )?
|
||||
{
|
||||
$expr = ::make_shared<raw::insert_statement>(std::move(cf),
|
||||
std::move(attrs),
|
||||
std::move(column_names),
|
||||
std::move(values),
|
||||
if_not_exists);
|
||||
}
|
||||
;
|
||||
|
||||
usingClause[::shared_ptr<cql3::attributes::raw> attrs]
|
||||
@@ -823,15 +796,10 @@ createIndexStatement returns [::shared_ptr<create_index_statement> expr]
|
||||
;
|
||||
|
||||
indexIdent returns [::shared_ptr<index_target::raw> id]
|
||||
@init {
|
||||
std::vector<::shared_ptr<cql3::column_identifier::raw>> columns;
|
||||
}
|
||||
: c=cident { $id = index_target::raw::values_of(c); }
|
||||
| K_KEYS '(' c=cident ')' { $id = index_target::raw::keys_of(c); }
|
||||
| K_ENTRIES '(' c=cident ')' { $id = index_target::raw::keys_and_values_of(c); }
|
||||
| K_FULL '(' c=cident ')' { $id = index_target::raw::full_collection(c); }
|
||||
| '(' c1=cident { columns.push_back(c1); } ( ',' cn=cident { columns.push_back(cn); } )* ')' { $id = index_target::raw::columns(std::move(columns)); }
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -905,8 +873,8 @@ alterKeyspaceStatement returns [shared_ptr<cql3::statements::alter_keyspace_stat
|
||||
|
||||
/**
|
||||
* ALTER COLUMN FAMILY <CF> ALTER <column> TYPE <newtype>;
|
||||
* ALTER COLUMN FAMILY <CF> ADD <column> <newtype>; | ALTER COLUMN FAMILY <CF> ADD (<column> <newtype>,<column1> <newtype1>..... <column n> <newtype n>)
|
||||
* ALTER COLUMN FAMILY <CF> DROP <column>; | ALTER COLUMN FAMILY <CF> DROP ( <column>,<column1>.....<column n>)
|
||||
* ALTER COLUMN FAMILY <CF> ADD <column> <newtype>;
|
||||
* ALTER COLUMN FAMILY <CF> DROP <column>;
|
||||
* ALTER COLUMN FAMILY <CF> WITH <property> = <value>;
|
||||
* ALTER COLUMN FAMILY <CF> RENAME <column> TO <column>;
|
||||
*/
|
||||
@@ -914,38 +882,21 @@ alterTableStatement returns [shared_ptr<alter_table_statement> expr]
|
||||
@init {
|
||||
alter_table_statement::type type;
|
||||
auto props = make_shared<cql3::statements::cf_prop_defs>();
|
||||
std::vector<alter_table_statement::column_change> column_changes;
|
||||
std::vector<std::pair<shared_ptr<cql3::column_identifier::raw>, shared_ptr<cql3::column_identifier::raw>>> renames;
|
||||
bool is_static = false;
|
||||
}
|
||||
: K_ALTER K_COLUMNFAMILY cf=columnFamilyName
|
||||
( K_ALTER id=cident K_TYPE v=comparatorType { type = alter_table_statement::type::alter; column_changes.emplace_back(alter_table_statement::column_change{id, v}); }
|
||||
| K_ADD { type = alter_table_statement::type::add; }
|
||||
( id=cident v=comparatorType s=cfisStatic { column_changes.emplace_back(alter_table_statement::column_change{id, v, s}); }
|
||||
| '(' id1=cident v1=comparatorType s1=cfisStatic { column_changes.emplace_back(alter_table_statement::column_change{id1, v1, s1}); }
|
||||
(',' idn=cident vn=comparatorType sn=cfisStatic { column_changes.emplace_back(alter_table_statement::column_change{idn, vn, sn}); } )* ')'
|
||||
)
|
||||
| K_DROP { type = alter_table_statement::type::drop; }
|
||||
( id=cident { column_changes.emplace_back(alter_table_statement::column_change{id}); }
|
||||
| '(' id1=cident { column_changes.emplace_back(alter_table_statement::column_change{id1}); }
|
||||
(',' idn=cident { column_changes.emplace_back(alter_table_statement::column_change{idn}); } )* ')'
|
||||
)
|
||||
( K_ALTER id=cident K_TYPE v=comparatorType { type = alter_table_statement::type::alter; }
|
||||
| K_ADD id=cident v=comparatorType ({ is_static=true; } K_STATIC)? { type = alter_table_statement::type::add; }
|
||||
| K_DROP id=cident { type = alter_table_statement::type::drop; }
|
||||
| K_WITH properties[props] { type = alter_table_statement::type::opts; }
|
||||
| K_RENAME { type = alter_table_statement::type::rename; }
|
||||
id1=cident K_TO toId1=cident { renames.emplace_back(id1, toId1); }
|
||||
( K_AND idn=cident K_TO toIdn=cident { renames.emplace_back(idn, toIdn); } )*
|
||||
)
|
||||
{
|
||||
$expr = ::make_shared<alter_table_statement>(std::move(cf), type, std::move(column_changes), std::move(props), std::move(renames));
|
||||
}
|
||||
;
|
||||
|
||||
cfisStatic returns [bool isStaticColumn]
|
||||
@init{
|
||||
bool isStatic = false;
|
||||
}
|
||||
: (K_STATIC { isStatic=true; })?
|
||||
{
|
||||
$isStaticColumn = isStatic;
|
||||
$expr = ::make_shared<alter_table_statement>(std::move(cf), type, std::move(id),
|
||||
std::move(v), std::move(props), std::move(renames), is_static);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -1559,22 +1510,12 @@ inMarkerForTuple returns [shared_ptr<cql3::tuples::in_raw> marker]
|
||||
| ':' name=ident { $marker = new_tuple_in_bind_variables(name); }
|
||||
;
|
||||
|
||||
// The comparator_type rule is used for users' queries (internal=false)
|
||||
// and for internal calls from db::cql_type_parser::parse() (internal=true).
|
||||
// The latter is used for reading schemas stored in the system tables, and
|
||||
// may support additional column types that cannot be created through CQL,
|
||||
// but only internally through code. Today the only such type is "empty":
|
||||
// Scylla code internally creates columns with type "empty" or collections
|
||||
// "empty" to represent unselected columns in materialized views.
|
||||
// If a user (internal=false) tries to use "empty" as a type, it is treated -
|
||||
// as do all unknown types - as an attempt to use a user-defined type, and
|
||||
// we report this name is reserved (as for _reserved_type_names()).
|
||||
comparator_type [bool internal] returns [shared_ptr<cql3_type::raw> t]
|
||||
: n=native_or_internal_type[internal] { $t = cql3_type::raw::from(n); }
|
||||
| c=collection_type[internal] { $t = c; }
|
||||
| tt=tuple_type[internal] { $t = tt; }
|
||||
comparatorType returns [shared_ptr<cql3_type::raw> t]
|
||||
: n=native_type { $t = cql3_type::raw::from(n); }
|
||||
| c=collection_type { $t = c; }
|
||||
| tt=tuple_type { $t = tt; }
|
||||
| id=userTypeName { $t = cql3::cql3_type::raw::user_type(id); }
|
||||
| K_FROZEN '<' f=comparator_type[internal] '>'
|
||||
| K_FROZEN '<' f=comparatorType '>'
|
||||
{
|
||||
try {
|
||||
$t = cql3::cql3_type::raw::frozen(f);
|
||||
@@ -1596,64 +1537,48 @@ comparator_type [bool internal] returns [shared_ptr<cql3_type::raw> t]
|
||||
#endif
|
||||
;
|
||||
|
||||
native_or_internal_type [bool internal] returns [data_type t]
|
||||
: n=native_type { $t = n; }
|
||||
// The "internal" types, only supported when internal==true:
|
||||
| K_EMPTY {
|
||||
if (internal) {
|
||||
$t = empty_type;
|
||||
} else {
|
||||
add_recognition_error("Invalid (reserved) user type name empty");
|
||||
}
|
||||
}
|
||||
native_type returns [shared_ptr<cql3_type> t]
|
||||
: K_ASCII { $t = cql3_type::ascii; }
|
||||
| K_BIGINT { $t = cql3_type::bigint; }
|
||||
| K_BLOB { $t = cql3_type::blob; }
|
||||
| K_BOOLEAN { $t = cql3_type::boolean; }
|
||||
| K_COUNTER { $t = cql3_type::counter; }
|
||||
| K_DECIMAL { $t = cql3_type::decimal; }
|
||||
| K_DOUBLE { $t = cql3_type::double_; }
|
||||
| K_DURATION { $t = cql3_type::duration; }
|
||||
| K_FLOAT { $t = cql3_type::float_; }
|
||||
| K_INET { $t = cql3_type::inet; }
|
||||
| K_INT { $t = cql3_type::int_; }
|
||||
| K_SMALLINT { $t = cql3_type::smallint; }
|
||||
| K_TEXT { $t = cql3_type::text; }
|
||||
| K_TIMESTAMP { $t = cql3_type::timestamp; }
|
||||
| K_TINYINT { $t = cql3_type::tinyint; }
|
||||
| K_UUID { $t = cql3_type::uuid; }
|
||||
| K_VARCHAR { $t = cql3_type::varchar; }
|
||||
| K_VARINT { $t = cql3_type::varint; }
|
||||
| K_TIMEUUID { $t = cql3_type::timeuuid; }
|
||||
| K_DATE { $t = cql3_type::date; }
|
||||
| K_TIME { $t = cql3_type::time; }
|
||||
;
|
||||
|
||||
comparatorType returns [shared_ptr<cql3_type::raw> t]
|
||||
: tt=comparator_type[false] { $t = tt; }
|
||||
;
|
||||
|
||||
native_type returns [data_type t]
|
||||
: K_ASCII { $t = ascii_type; }
|
||||
| K_BIGINT { $t = long_type; }
|
||||
| K_BLOB { $t = bytes_type; }
|
||||
| K_BOOLEAN { $t = boolean_type; }
|
||||
| K_COUNTER { $t = counter_type; }
|
||||
| K_DECIMAL { $t = decimal_type; }
|
||||
| K_DOUBLE { $t = double_type; }
|
||||
| K_DURATION { $t = duration_type; }
|
||||
| K_FLOAT { $t = float_type; }
|
||||
| K_INET { $t = inet_addr_type; }
|
||||
| K_INT { $t = int32_type; }
|
||||
| K_SMALLINT { $t = short_type; }
|
||||
| K_TEXT { $t = utf8_type; }
|
||||
| K_TIMESTAMP { $t = timestamp_type; }
|
||||
| K_TINYINT { $t = byte_type; }
|
||||
| K_UUID { $t = uuid_type; }
|
||||
| K_VARCHAR { $t = utf8_type; }
|
||||
| K_VARINT { $t = varint_type; }
|
||||
| K_TIMEUUID { $t = timeuuid_type; }
|
||||
| K_DATE { $t = simple_date_type; }
|
||||
| K_TIME { $t = time_type; }
|
||||
;
|
||||
|
||||
collection_type [bool internal] returns [shared_ptr<cql3::cql3_type::raw> pt]
|
||||
: K_MAP '<' t1=comparator_type[internal] ',' t2=comparator_type[internal] '>'
|
||||
collection_type returns [shared_ptr<cql3::cql3_type::raw> pt]
|
||||
: K_MAP '<' t1=comparatorType ',' t2=comparatorType '>'
|
||||
{
|
||||
// if we can't parse either t1 or t2, antlr will "recover" and we may have t1 or t2 null.
|
||||
if (t1 && t2) {
|
||||
$pt = cql3::cql3_type::raw::map(t1, t2);
|
||||
}
|
||||
}
|
||||
| K_LIST '<' t=comparator_type[internal] '>'
|
||||
| K_LIST '<' t=comparatorType '>'
|
||||
{ if (t) { $pt = cql3::cql3_type::raw::list(t); } }
|
||||
| K_SET '<' t=comparator_type[internal] '>'
|
||||
| K_SET '<' t=comparatorType '>'
|
||||
{ if (t) { $pt = cql3::cql3_type::raw::set(t); } }
|
||||
;
|
||||
|
||||
tuple_type [bool internal] returns [shared_ptr<cql3::cql3_type::raw> t]
|
||||
tuple_type returns [shared_ptr<cql3::cql3_type::raw> t]
|
||||
@init{ std::vector<shared_ptr<cql3::cql3_type::raw>> types; }
|
||||
: K_TUPLE '<'
|
||||
t1=comparator_type[internal] { types.push_back(t1); } (',' tn=comparator_type[internal] { types.push_back(tn); })*
|
||||
t1=comparatorType { types.push_back(t1); } (',' tn=comparatorType { types.push_back(tn); })*
|
||||
'>' { $t = cql3::cql3_type::raw::tuple(std::move(types)); }
|
||||
;
|
||||
|
||||
@@ -1679,7 +1604,7 @@ unreserved_keyword returns [sstring str]
|
||||
|
||||
unreserved_function_keyword returns [sstring str]
|
||||
: u=basic_unreserved_keyword { $str = u; }
|
||||
| t=native_or_internal_type[true] { $str = t->as_cql3_type().to_string(); }
|
||||
| t=native_type { $str = t->to_string(); }
|
||||
;
|
||||
|
||||
basic_unreserved_keyword returns [sstring str]
|
||||
@@ -1725,11 +1650,6 @@ basic_unreserved_keyword returns [sstring str]
|
||||
| K_LANGUAGE
|
||||
| K_NON
|
||||
| K_DETERMINISTIC
|
||||
| K_JSON
|
||||
| K_CACHE
|
||||
| K_BYPASS
|
||||
| K_PER
|
||||
| K_PARTITION
|
||||
) { $str = $k.text; }
|
||||
;
|
||||
|
||||
@@ -1866,17 +1786,6 @@ K_NON: N O N;
|
||||
K_OR: O R;
|
||||
K_REPLACE: R E P L A C E;
|
||||
K_DETERMINISTIC: D E T E R M I N I S T I C;
|
||||
K_JSON: J S O N;
|
||||
K_DEFAULT: D E F A U L T;
|
||||
K_UNSET: U N S E T;
|
||||
|
||||
K_EMPTY: E M P T Y;
|
||||
|
||||
K_BYPASS: B Y P A S S;
|
||||
K_CACHE: C A C H E;
|
||||
|
||||
K_PER: P E R;
|
||||
K_PARTITION: P A R T I T I O N;
|
||||
|
||||
K_SCYLLA_TIMEUUID_LIST_INDEX: S C Y L L A '_' T I M E U U I D '_' L I S T '_' I N D E X;
|
||||
K_SCYLLA_COUNTER_SHARD_LIST: S C Y L L A '_' C O U N T E R '_' S H A R D '_' L I S T;
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
#include "cql3/lists.hh"
|
||||
#include "cql3/maps.hh"
|
||||
#include "cql3/sets.hh"
|
||||
#include "types/list.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
|
||||
@@ -77,14 +77,12 @@ 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(val, options.get_cql_serialization_format());
|
||||
data_type_for<int64_t>()->validate(*tval);
|
||||
} catch (marshal_exception& e) {
|
||||
throw exceptions::invalid_request_exception("Invalid timestamp value");
|
||||
}
|
||||
return value_cast<int64_t>(data_type_for<int64_t>()->deserialize(val));
|
||||
});
|
||||
return value_cast<int64_t>(data_type_for<int64_t>()->deserialize(*tval));
|
||||
}
|
||||
|
||||
int32_t attributes::get_time_to_live(const query_options& options) {
|
||||
@@ -98,16 +96,14 @@ 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(val, options.get_cql_serialization_format());
|
||||
data_type_for<int32_t>()->validate(*tval);
|
||||
}
|
||||
catch (marshal_exception& e) {
|
||||
throw exceptions::invalid_request_exception("Invalid TTL value");
|
||||
}
|
||||
|
||||
return value_cast<int32_t>(data_type_for<int32_t>()->deserialize(val));
|
||||
});
|
||||
auto ttl = value_cast<int32_t>(data_type_for<int32_t>()->deserialize(*tval));
|
||||
if (ttl < 0) {
|
||||
throw exceptions::invalid_request_exception("A TTL must be greater or equal to 0");
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "cql3/term.hh"
|
||||
#include <optional>
|
||||
#include <experimental/optional>
|
||||
|
||||
namespace cql3 {
|
||||
/**
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cql3/prepared_statements_cache.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
struct authorized_prepared_statements_cache_size {
|
||||
size_t operator()(const statements::prepared_statement::checked_weak_ptr& val) {
|
||||
// TODO: improve the size approximation - most of the entry is occupied by the key here.
|
||||
return 100;
|
||||
}
|
||||
};
|
||||
|
||||
class authorized_prepared_statements_cache_key {
|
||||
public:
|
||||
using cache_key_type = std::pair<auth::authenticated_user, typename cql3::prepared_cache_key_type::cache_key_type>;
|
||||
private:
|
||||
cache_key_type _key;
|
||||
|
||||
public:
|
||||
authorized_prepared_statements_cache_key(auth::authenticated_user user, cql3::prepared_cache_key_type prepared_cache_key)
|
||||
: _key(std::move(user), std::move(prepared_cache_key.key())) {}
|
||||
|
||||
cache_key_type& key() { return _key; }
|
||||
|
||||
const cache_key_type& key() const { return _key; }
|
||||
|
||||
bool operator==(const authorized_prepared_statements_cache_key& other) const {
|
||||
return _key == other._key;
|
||||
}
|
||||
|
||||
bool operator!=(const authorized_prepared_statements_cache_key& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
static size_t hash(const auth::authenticated_user& user, const cql3::prepared_cache_key_type::cache_key_type& prep_cache_key) {
|
||||
return utils::hash_combine(std::hash<auth::authenticated_user>()(user), utils::tuple_hash()(prep_cache_key));
|
||||
}
|
||||
};
|
||||
|
||||
/// \class authorized_prepared_statements_cache
|
||||
/// \brief A cache of previously authorized statements.
|
||||
///
|
||||
/// Entries are inserted every time a new statement is authorized.
|
||||
/// Entries are evicted in any of the following cases:
|
||||
/// - When the corresponding prepared statement is not valid anymore.
|
||||
/// - Periodically, with the same period as the permission cache is refreshed.
|
||||
/// - If the corresponding entry hasn't been used for \ref entry_expiry.
|
||||
class authorized_prepared_statements_cache {
|
||||
public:
|
||||
struct stats {
|
||||
uint64_t authorized_prepared_statements_cache_evictions = 0;
|
||||
};
|
||||
|
||||
static stats& shard_stats() {
|
||||
static thread_local stats _stats;
|
||||
return _stats;
|
||||
}
|
||||
|
||||
struct authorized_prepared_statements_cache_stats_updater {
|
||||
static void inc_hits() noexcept {}
|
||||
static void inc_misses() noexcept {}
|
||||
static void inc_blocks() noexcept {}
|
||||
static void inc_evictions() noexcept {
|
||||
++shard_stats().authorized_prepared_statements_cache_evictions;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
using cache_key_type = authorized_prepared_statements_cache_key;
|
||||
using checked_weak_ptr = typename statements::prepared_statement::checked_weak_ptr;
|
||||
using cache_type = utils::loading_cache<cache_key_type,
|
||||
checked_weak_ptr,
|
||||
utils::loading_cache_reload_enabled::yes,
|
||||
authorized_prepared_statements_cache_size,
|
||||
std::hash<cache_key_type>,
|
||||
std::equal_to<cache_key_type>,
|
||||
authorized_prepared_statements_cache_stats_updater>;
|
||||
|
||||
public:
|
||||
using key_type = cache_key_type;
|
||||
using value_type = checked_weak_ptr;
|
||||
using entry_is_too_big = typename cache_type::entry_is_too_big;
|
||||
using iterator = typename cache_type::iterator;
|
||||
|
||||
private:
|
||||
cache_type _cache;
|
||||
logging::logger& _logger;
|
||||
|
||||
public:
|
||||
// Choose the memory budget such that would allow us ~4K entries when a shard gets 1GB of RAM
|
||||
authorized_prepared_statements_cache(std::chrono::milliseconds entry_expiration, std::chrono::milliseconds entry_refresh, size_t cache_size, logging::logger& logger)
|
||||
: _cache(cache_size, entry_expiration, entry_refresh, logger, [this] (const key_type& k) {
|
||||
_cache.remove(k);
|
||||
return make_ready_future<value_type>();
|
||||
})
|
||||
, _logger(logger)
|
||||
{}
|
||||
|
||||
future<> insert(auth::authenticated_user user, cql3::prepared_cache_key_type prep_cache_key, value_type v) noexcept {
|
||||
return _cache.get_ptr(key_type(std::move(user), std::move(prep_cache_key)), [v = std::move(v)] (const cache_key_type&) mutable {
|
||||
return make_ready_future<value_type>(std::move(v));
|
||||
}).discard_result();
|
||||
}
|
||||
|
||||
iterator find(const auth::authenticated_user& user, const cql3::prepared_cache_key_type& prep_cache_key) {
|
||||
struct key_view {
|
||||
const auth::authenticated_user& user_ref;
|
||||
const cql3::prepared_cache_key_type& prep_cache_key_ref;
|
||||
};
|
||||
|
||||
struct hasher {
|
||||
size_t operator()(const key_view& kv) {
|
||||
return cql3::authorized_prepared_statements_cache_key::hash(kv.user_ref, kv.prep_cache_key_ref.key());
|
||||
}
|
||||
};
|
||||
|
||||
struct equal {
|
||||
bool operator()(const key_type& k1, const key_view& k2) {
|
||||
return k1.key().first == k2.user_ref && k1.key().second == k2.prep_cache_key_ref.key();
|
||||
}
|
||||
|
||||
bool operator()(const key_view& k2, const key_type& k1) {
|
||||
return operator()(k1, k2);
|
||||
}
|
||||
};
|
||||
|
||||
return _cache.find(key_view{user, prep_cache_key}, hasher(), equal());
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return _cache.end();
|
||||
}
|
||||
|
||||
void remove(const auth::authenticated_user& user, const cql3::prepared_cache_key_type& prep_cache_key) {
|
||||
iterator it = find(user, prep_cache_key);
|
||||
_cache.remove(it);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return _cache.size();
|
||||
}
|
||||
|
||||
size_t memory_footprint() const {
|
||||
return _cache.memory_footprint();
|
||||
}
|
||||
|
||||
future<> stop() {
|
||||
return _cache.stop();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<cql3::authorized_prepared_statements_cache_key> final {
|
||||
size_t operator()(const cql3::authorized_prepared_statements_cache_key& k) const {
|
||||
return cql3::authorized_prepared_statements_cache_key::hash(k.key().first, k.key().second);
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const cql3::authorized_prepared_statements_cache_key& k) {
|
||||
return out << "{ " << k.key().first << ", " << k.key().second << " }";
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ void validate_operation_on_durations(const abstract_type& type, const cql3::oper
|
||||
check_false(type.is_user_type(), "Slice conditions are not supported on UDTs containing durations");
|
||||
|
||||
// We're a duration.
|
||||
throw exceptions::invalid_request_exception(format("Slice conditions are not supported on durations"));
|
||||
throw exceptions::invalid_request_exception(sprint("Slice conditions are not supported on durations"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu
|
||||
}
|
||||
|
||||
if (!receiver.type->is_collection()) {
|
||||
throw exceptions::invalid_request_exception(format("Invalid element access syntax for non-collection column {}", receiver.name_as_text()));
|
||||
throw exceptions::invalid_request_exception(sprint("Invalid element access syntax for non-collection column %s", receiver.name_as_text()));
|
||||
}
|
||||
|
||||
shared_ptr<column_specification> element_spec, value_spec;
|
||||
@@ -131,7 +131,7 @@ column_condition::raw::prepare(database& db, const sstring& keyspace, const colu
|
||||
element_spec = maps::key_spec_of(*receiver.column_specification);
|
||||
value_spec = maps::value_spec_of(*receiver.column_specification);
|
||||
} else if (&ctype->_kind == &collection_type_impl::kind::set) {
|
||||
throw exceptions::invalid_request_exception(format("Invalid element access syntax for set column {}", receiver.name()));
|
||||
throw exceptions::invalid_request_exception(sprint("Invalid element access syntax for set column %s", receiver.name()));
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "cql3/column_identifier.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include "cql3/selection/simple_selector.hh"
|
||||
#include "cql3/util.hh"
|
||||
|
||||
#include <regex>
|
||||
|
||||
@@ -63,11 +62,14 @@ sstring column_identifier::to_string() const {
|
||||
}
|
||||
|
||||
sstring column_identifier::to_cql_string() const {
|
||||
return util::maybe_quote(_text);
|
||||
}
|
||||
|
||||
sstring column_identifier::raw::to_cql_string() const {
|
||||
return util::maybe_quote(_text);
|
||||
static const std::regex unquoted_identifier_re("[a-z][a-z0-9_]*");
|
||||
if (std::regex_match(_text.begin(), _text.end(), unquoted_identifier_re)) {
|
||||
return _text;
|
||||
}
|
||||
static const std::regex double_quote_re("\"");
|
||||
std::string result = _text;
|
||||
std::regex_replace(result, double_quote_re, "\"\"");
|
||||
return '"' + result + '"';
|
||||
}
|
||||
|
||||
column_identifier::raw::raw(sstring raw_text, bool keep_case)
|
||||
@@ -125,13 +127,9 @@ std::ostream& operator<<(std::ostream& out, const column_identifier::raw& id) {
|
||||
column_identifier::new_selector_factory(database& db, schema_ptr schema, std::vector<const column_definition*>& defs) {
|
||||
auto def = get_column_definition(schema, *this);
|
||||
if (!def) {
|
||||
throw exceptions::invalid_request_exception(format("Undefined name {} in selection clause", _text));
|
||||
}
|
||||
// Do not allow explicitly selecting hidden columns. We also skip them on
|
||||
// "SELECT *" (see selection::wildcard()).
|
||||
if (def->is_hidden_from_cql()) {
|
||||
throw exceptions::invalid_request_exception(format("Undefined name {} in selection clause", _text));
|
||||
throw exceptions::invalid_request_exception(sprint("Undefined name %s in selection clause", _text));
|
||||
}
|
||||
|
||||
return selection::simple_selector::new_factory(def->name_as_text(), add_and_get_index(*def, defs), def->type);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,6 @@ public:
|
||||
bool operator!=(const raw& other) const;
|
||||
|
||||
virtual sstring to_string() const;
|
||||
sstring to_cql_string() const;
|
||||
|
||||
friend std::hash<column_identifier::raw>;
|
||||
friend std::ostream& operator<<(std::ostream& out, const column_identifier::raw& id);
|
||||
|
||||
@@ -85,19 +85,20 @@ assignment_testable::test_result
|
||||
constants::literal::test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver)
|
||||
{
|
||||
auto receiver_type = receiver->type->as_cql3_type();
|
||||
if (receiver_type.is_collection()) {
|
||||
if (receiver_type->is_collection()) {
|
||||
return test_result::NOT_ASSIGNABLE;
|
||||
}
|
||||
if (!receiver_type.is_native()) {
|
||||
if (!receiver_type->is_native()) {
|
||||
return test_result::WEAKLY_ASSIGNABLE;
|
||||
}
|
||||
auto kind = receiver_type.get_kind();
|
||||
auto kind = receiver_type.get()->get_kind();
|
||||
switch (_type) {
|
||||
case type::STRING:
|
||||
if (cql3_type::kind_enum_set::frozen<
|
||||
cql3_type::kind::ASCII,
|
||||
cql3_type::kind::TEXT,
|
||||
cql3_type::kind::INET,
|
||||
cql3_type::kind::VARCHAR,
|
||||
cql3_type::kind::TIMESTAMP,
|
||||
cql3_type::kind::DATE,
|
||||
cql3_type::kind::TIME>::contains(kind)) {
|
||||
@@ -158,8 +159,8 @@ constants::literal::test_assignment(database& db, const sstring& keyspace, ::sha
|
||||
constants::literal::prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver)
|
||||
{
|
||||
if (!is_assignable(test_assignment(db, keyspace, receiver))) {
|
||||
throw exceptions::invalid_request_exception(format("Invalid {} constant ({}) for \"{}\" of type {}",
|
||||
_type, _text, *receiver->name, receiver->type->as_cql3_type().to_string()));
|
||||
throw exceptions::invalid_request_exception(sprint("Invalid %s constant (%s) for \"%s\" of type %s",
|
||||
_type, _text, *receiver->name, receiver->type->as_cql3_type()->to_string()));
|
||||
}
|
||||
return ::make_shared<value>(cql3::raw_value::make_value(parsed_value(receiver->type)));
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#include "cql3/operation.hh"
|
||||
#include "cql3/values.hh"
|
||||
#include "cql3/term.hh"
|
||||
#include <seastar/core/shared_ptr.hh>
|
||||
#include "core/shared_ptr.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
@@ -85,8 +85,8 @@ public:
|
||||
virtual ::shared_ptr<terminal> bind(const query_options& options) override { return {}; }
|
||||
virtual sstring to_string() const override { return "null"; }
|
||||
};
|
||||
public:
|
||||
static thread_local const ::shared_ptr<terminal> NULL_VALUE;
|
||||
public:
|
||||
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override {
|
||||
if (!is_assignable(test_assignment(db, keyspace, receiver))) {
|
||||
throw exceptions::invalid_request_exception("Invalid null value for counter increment/decrement");
|
||||
@@ -164,7 +164,7 @@ public:
|
||||
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver);
|
||||
|
||||
virtual sstring to_string() const override {
|
||||
return _type == type::STRING ? sstring(format("'{}'", _text)) : _text;
|
||||
return _type == type::STRING ? sstring(sprint("'%s'", _text)) : _text;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,7 +180,7 @@ public:
|
||||
try {
|
||||
auto value = options.get_value_at(_bind_index);
|
||||
if (value) {
|
||||
_receiver->type->validate(*value, options.get_cql_serialization_format());
|
||||
_receiver->type->validate(*value);
|
||||
}
|
||||
return value;
|
||||
} catch (const marshal_exception& e) {
|
||||
@@ -203,14 +203,10 @@ public:
|
||||
|
||||
virtual void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params) override {
|
||||
auto value = _t->bind_and_get(params._options);
|
||||
execute(m, prefix, params, column, std::move(value));
|
||||
}
|
||||
|
||||
static void execute(mutation& m, const clustering_key_prefix& prefix, const update_parameters& params, const column_definition& column, cql3::raw_value_view value) {
|
||||
if (value.is_null()) {
|
||||
m.set_cell(prefix, column, std::move(make_dead_cell(params)));
|
||||
} else if (value.is_value()) {
|
||||
m.set_cell(prefix, column, std::move(make_cell(*column.type, *value, params)));
|
||||
m.set_cell(prefix, column, std::move(make_cell(*value, params)));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -225,9 +221,7 @@ public:
|
||||
} else if (value.is_unset_value()) {
|
||||
return;
|
||||
}
|
||||
auto increment = with_linearized(*value, [] (bytes_view value_view) {
|
||||
return value_cast<int64_t>(long_type->deserialize_value(value_view));
|
||||
});
|
||||
auto increment = value_cast<int64_t>(long_type->deserialize_value(*value));
|
||||
m.set_cell(prefix, column, make_counter_update_cell(increment, params));
|
||||
}
|
||||
};
|
||||
@@ -242,11 +236,9 @@ public:
|
||||
} else if (value.is_unset_value()) {
|
||||
return;
|
||||
}
|
||||
auto increment = with_linearized(*value, [] (bytes_view value_view) {
|
||||
return value_cast<int64_t>(long_type->deserialize_value(value_view));
|
||||
});
|
||||
auto increment = value_cast<int64_t>(long_type->deserialize_value(*value));
|
||||
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));
|
||||
throw exceptions::invalid_request_exception(sprint("The negation of %d overflows supported counter precision (signed 8 bytes integer)", increment));
|
||||
}
|
||||
m.set_cell(prefix, column, make_counter_update_cell(-increment, params));
|
||||
}
|
||||
|
||||
@@ -26,15 +26,20 @@
|
||||
#include "cql3_type.hh"
|
||||
#include "cql3/util.hh"
|
||||
#include "ut_name.hh"
|
||||
#include "database.hh"
|
||||
#include "user_types_metadata.hh"
|
||||
#include "types/map.hh"
|
||||
#include "types/set.hh"
|
||||
#include "types/list.hh"
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
cql3_type cql3_type::raw::prepare(database& db, const sstring& keyspace) {
|
||||
sstring cql3_type::to_string() const {
|
||||
if (_type->is_user_type()) {
|
||||
return "frozen<" + util::maybe_quote(_name) + ">";
|
||||
}
|
||||
if (_type->is_tuple()) {
|
||||
return "frozen<" + _name + ">";
|
||||
}
|
||||
return _name;
|
||||
}
|
||||
|
||||
shared_ptr<cql3_type> cql3_type::raw::prepare(database& db, const sstring& keyspace) {
|
||||
try {
|
||||
auto&& ks = db.find_keyspace(keyspace);
|
||||
return prepare_internal(keyspace, ks.metadata()->user_types());
|
||||
@@ -53,16 +58,16 @@ bool cql3_type::raw::references_user_type(const sstring& name) const {
|
||||
|
||||
class cql3_type::raw_type : public raw {
|
||||
private:
|
||||
cql3_type _type;
|
||||
shared_ptr<cql3_type> _type;
|
||||
public:
|
||||
raw_type(cql3_type type)
|
||||
raw_type(shared_ptr<cql3_type> type)
|
||||
: _type{type}
|
||||
{ }
|
||||
public:
|
||||
virtual cql3_type prepare(database& db, const sstring& keyspace) {
|
||||
virtual shared_ptr<cql3_type> prepare(database& db, const sstring& keyspace) {
|
||||
return _type;
|
||||
}
|
||||
cql3_type prepare_internal(const sstring&, lw_shared_ptr<user_types_metadata>) override {
|
||||
shared_ptr<cql3_type> prepare_internal(const sstring&, lw_shared_ptr<user_types_metadata>) override {
|
||||
return _type;
|
||||
}
|
||||
|
||||
@@ -71,15 +76,15 @@ public:
|
||||
}
|
||||
|
||||
virtual bool is_counter() const {
|
||||
return _type.is_counter();
|
||||
return _type->is_counter();
|
||||
}
|
||||
|
||||
virtual sstring to_string() const {
|
||||
return _type.to_string();
|
||||
return _type->to_string();
|
||||
}
|
||||
|
||||
virtual bool is_duration() const override {
|
||||
return _type.get_type()->equals(duration_type);
|
||||
return _type->get_type()->equals(duration_type);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -110,35 +115,35 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual cql3_type prepare_internal(const sstring& keyspace, lw_shared_ptr<user_types_metadata> user_types) override {
|
||||
virtual shared_ptr<cql3_type> prepare_internal(const sstring& keyspace, lw_shared_ptr<user_types_metadata> user_types) override {
|
||||
assert(_values); // "Got null values type for a collection";
|
||||
|
||||
if (!_frozen && _values->supports_freezing() && !_values->_frozen) {
|
||||
throw exceptions::invalid_request_exception(format("Non-frozen collections are not allowed inside collections: {}", *this));
|
||||
throw exceptions::invalid_request_exception(sprint("Non-frozen collections are not allowed inside collections: %s", *this));
|
||||
}
|
||||
if (_values->is_counter()) {
|
||||
throw exceptions::invalid_request_exception(format("Counters are not allowed inside collections: {}", *this));
|
||||
throw exceptions::invalid_request_exception(sprint("Counters are not allowed inside collections: %s", *this));
|
||||
}
|
||||
|
||||
if (_keys) {
|
||||
if (!_frozen && _keys->supports_freezing() && !_keys->_frozen) {
|
||||
throw exceptions::invalid_request_exception(format("Non-frozen collections are not allowed inside collections: {}", *this));
|
||||
throw exceptions::invalid_request_exception(sprint("Non-frozen collections are not allowed inside collections: %s", *this));
|
||||
}
|
||||
}
|
||||
|
||||
if (_kind == &collection_type_impl::kind::list) {
|
||||
return cql3_type(list_type_impl::get_instance(_values->prepare_internal(keyspace, user_types).get_type(), !_frozen));
|
||||
return make_shared(cql3_type(to_string(), list_type_impl::get_instance(_values->prepare_internal(keyspace, user_types)->get_type(), !_frozen), false));
|
||||
} else if (_kind == &collection_type_impl::kind::set) {
|
||||
if (_values->is_duration()) {
|
||||
throw exceptions::invalid_request_exception(format("Durations are not allowed inside sets: {}", *this));
|
||||
throw exceptions::invalid_request_exception(sprint("Durations are not allowed inside sets: %s", *this));
|
||||
}
|
||||
return cql3_type(set_type_impl::get_instance(_values->prepare_internal(keyspace, user_types).get_type(), !_frozen));
|
||||
return make_shared(cql3_type(to_string(), set_type_impl::get_instance(_values->prepare_internal(keyspace, user_types)->get_type(), !_frozen), false));
|
||||
} else if (_kind == &collection_type_impl::kind::map) {
|
||||
assert(_keys); // "Got null keys type for a collection";
|
||||
if (_keys->is_duration()) {
|
||||
throw exceptions::invalid_request_exception(format("Durations are not allowed as map keys: {}", *this));
|
||||
throw exceptions::invalid_request_exception(sprint("Durations are not allowed as map keys: %s", *this));
|
||||
}
|
||||
return cql3_type(map_type_impl::get_instance(_keys->prepare_internal(keyspace, user_types).get_type(), _values->prepare_internal(keyspace, user_types).get_type(), !_frozen));
|
||||
return make_shared(cql3_type(to_string(), map_type_impl::get_instance(_keys->prepare_internal(keyspace, user_types)->get_type(), _values->prepare_internal(keyspace, user_types)->get_type(), !_frozen), false));
|
||||
}
|
||||
abort();
|
||||
}
|
||||
@@ -155,11 +160,11 @@ public:
|
||||
sstring start = _frozen ? "frozen<" : "";
|
||||
sstring end = _frozen ? ">" : "";
|
||||
if (_kind == &collection_type_impl::kind::list) {
|
||||
return format("{}list<{}>{}", start, _values, end);
|
||||
return sprint("%slist<%s>%s", start, _values, end);
|
||||
} else if (_kind == &collection_type_impl::kind::set) {
|
||||
return format("{}set<{}>{}", start, _values, end);
|
||||
return sprint("%sset<%s>%s", start, _values, end);
|
||||
} else if (_kind == &collection_type_impl::kind::map) {
|
||||
return format("{}map<{}, {}>{}", start, _keys, _values, end);
|
||||
return sprint("%smap<%s, %s>%s", start, _keys, _values, end);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
@@ -172,7 +177,7 @@ public:
|
||||
: _name(std::move(name)) {
|
||||
}
|
||||
|
||||
virtual std::optional<sstring> keyspace() const override {
|
||||
virtual std::experimental::optional<sstring> keyspace() const override {
|
||||
return _name.get_keyspace();
|
||||
}
|
||||
|
||||
@@ -180,7 +185,7 @@ public:
|
||||
_frozen = true;
|
||||
}
|
||||
|
||||
virtual cql3_type prepare_internal(const sstring& keyspace, lw_shared_ptr<user_types_metadata> user_types) override {
|
||||
virtual shared_ptr<cql3_type> prepare_internal(const sstring& keyspace, lw_shared_ptr<user_types_metadata> user_types) override {
|
||||
if (_name.has_keyspace()) {
|
||||
// The provided keyspace is the one of the current statement this is part of. If it's different from the keyspace of
|
||||
// the UTName, we reject since we want to limit user types to their own keyspace (see #6643)
|
||||
@@ -194,16 +199,16 @@ public:
|
||||
}
|
||||
if (!user_types) {
|
||||
// bootstrap mode.
|
||||
throw exceptions::invalid_request_exception(format("Unknown type {}", _name));
|
||||
throw exceptions::invalid_request_exception(sprint("Unknown type %s", _name));
|
||||
}
|
||||
try {
|
||||
auto&& type = user_types->get_type(_name.get_user_type_name());
|
||||
if (!_frozen) {
|
||||
throw exceptions::invalid_request_exception("Non-frozen User-Defined types are not supported, please use frozen<>");
|
||||
}
|
||||
return cql3_type(std::move(type));
|
||||
return make_shared<cql3_type>(_name.to_string(), std::move(type));
|
||||
} catch (std::out_of_range& e) {
|
||||
throw exceptions::invalid_request_exception(format("Unknown type {}", _name));
|
||||
throw exceptions::invalid_request_exception(sprint("Unknown type %s", _name));
|
||||
}
|
||||
}
|
||||
bool references_user_type(const sstring& name) const override {
|
||||
@@ -239,7 +244,7 @@ public:
|
||||
}
|
||||
_frozen = true;
|
||||
}
|
||||
virtual cql3_type prepare_internal(const sstring& keyspace, lw_shared_ptr<user_types_metadata> user_types) override {
|
||||
virtual shared_ptr<cql3_type> prepare_internal(const sstring& keyspace, lw_shared_ptr<user_types_metadata> user_types) override {
|
||||
if (!_frozen) {
|
||||
freeze();
|
||||
}
|
||||
@@ -248,9 +253,9 @@ public:
|
||||
if (t->is_counter()) {
|
||||
throw exceptions::invalid_request_exception("Counters are not allowed inside tuples");
|
||||
}
|
||||
ts.push_back(t->prepare_internal(keyspace, user_types).get_type());
|
||||
ts.push_back(t->prepare_internal(keyspace, user_types)->get_type());
|
||||
}
|
||||
return tuple_type_impl::get_instance(std::move(ts))->as_cql3_type();
|
||||
return make_cql3_tuple_type(tuple_type_impl::get_instance(std::move(ts)));
|
||||
}
|
||||
|
||||
bool references_user_type(const sstring& name) const override {
|
||||
@@ -260,7 +265,7 @@ public:
|
||||
}
|
||||
|
||||
virtual sstring to_string() const override {
|
||||
return format("tuple<{}>", join(", ", _types));
|
||||
return sprint("tuple<%s>", join(", ", _types));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -274,19 +279,19 @@ cql3_type::raw::is_counter() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<sstring>
|
||||
std::experimental::optional<sstring>
|
||||
cql3_type::raw::keyspace() const {
|
||||
return std::nullopt;
|
||||
return std::experimental::nullopt;
|
||||
}
|
||||
|
||||
void
|
||||
cql3_type::raw::freeze() {
|
||||
sstring message = format("frozen<> is only allowed on collections, tuples, and user-defined types (got {})", to_string());
|
||||
sstring message = sprint("frozen<> is only allowed on collections, tuples, and user-defined types (got %s)", to_string());
|
||||
throw exceptions::invalid_request_exception(message);
|
||||
}
|
||||
|
||||
shared_ptr<cql3_type::raw>
|
||||
cql3_type::raw::from(cql3_type type) {
|
||||
cql3_type::raw::from(shared_ptr<cql3_type> type) {
|
||||
return ::make_shared<raw_type>(type);
|
||||
}
|
||||
|
||||
@@ -321,31 +326,32 @@ cql3_type::raw::frozen(shared_ptr<raw> t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
thread_local cql3_type cql3_type::ascii{ascii_type};
|
||||
thread_local cql3_type cql3_type::bigint{long_type};
|
||||
thread_local cql3_type cql3_type::blob{bytes_type};
|
||||
thread_local cql3_type cql3_type::boolean{boolean_type};
|
||||
thread_local cql3_type cql3_type::double_{double_type};
|
||||
thread_local cql3_type cql3_type::empty{empty_type};
|
||||
thread_local cql3_type cql3_type::float_{float_type};
|
||||
thread_local cql3_type cql3_type::int_{int32_type};
|
||||
thread_local cql3_type cql3_type::smallint{short_type};
|
||||
thread_local cql3_type cql3_type::text{utf8_type};
|
||||
thread_local cql3_type cql3_type::timestamp{timestamp_type};
|
||||
thread_local cql3_type cql3_type::tinyint{byte_type};
|
||||
thread_local cql3_type cql3_type::uuid{uuid_type};
|
||||
thread_local cql3_type cql3_type::timeuuid{timeuuid_type};
|
||||
thread_local cql3_type cql3_type::date{simple_date_type};
|
||||
thread_local cql3_type cql3_type::time{time_type};
|
||||
thread_local cql3_type cql3_type::inet{inet_addr_type};
|
||||
thread_local cql3_type cql3_type::varint{varint_type};
|
||||
thread_local cql3_type cql3_type::decimal{decimal_type};
|
||||
thread_local cql3_type cql3_type::counter{counter_type};
|
||||
thread_local cql3_type cql3_type::duration{duration_type};
|
||||
thread_local shared_ptr<cql3_type> cql3_type::ascii = make("ascii", ascii_type, cql3_type::kind::ASCII);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::bigint = make("bigint", long_type, cql3_type::kind::BIGINT);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::blob = make("blob", bytes_type, cql3_type::kind::BLOB);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::boolean = make("boolean", boolean_type, cql3_type::kind::BOOLEAN);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::double_ = make("double", double_type, cql3_type::kind::DOUBLE);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::empty = make("empty", empty_type, cql3_type::kind::EMPTY);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::float_ = make("float", float_type, cql3_type::kind::FLOAT);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::int_ = make("int", int32_type, cql3_type::kind::INT);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::smallint = make("smallint", short_type, cql3_type::kind::SMALLINT);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::text = make("text", utf8_type, cql3_type::kind::TEXT);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::timestamp = make("timestamp", timestamp_type, cql3_type::kind::TIMESTAMP);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::tinyint = make("tinyint", byte_type, cql3_type::kind::TINYINT);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::uuid = make("uuid", uuid_type, cql3_type::kind::UUID);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::varchar = make("varchar", utf8_type, cql3_type::kind::TEXT);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::timeuuid = make("timeuuid", timeuuid_type, cql3_type::kind::TIMEUUID);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::date = make("date", simple_date_type, cql3_type::kind::DATE);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::time = make("time", time_type, cql3_type::kind::TIME);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::inet = make("inet", inet_addr_type, cql3_type::kind::INET);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::varint = make("varint", varint_type, cql3_type::kind::VARINT);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::decimal = make("decimal", decimal_type, cql3_type::kind::DECIMAL);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::counter = make("counter", counter_type, cql3_type::kind::COUNTER);
|
||||
thread_local shared_ptr<cql3_type> cql3_type::duration = make("duration", duration_type, cql3_type::kind::DURATION);
|
||||
|
||||
const std::vector<cql3_type>&
|
||||
const std::vector<shared_ptr<cql3_type>>&
|
||||
cql3_type::values() {
|
||||
static thread_local std::vector<cql3_type> v = {
|
||||
static thread_local std::vector<shared_ptr<cql3_type>> v = {
|
||||
cql3_type::ascii,
|
||||
cql3_type::bigint,
|
||||
cql3_type::blob,
|
||||
@@ -362,6 +368,7 @@ cql3_type::values() {
|
||||
cql3_type::timestamp,
|
||||
cql3_type::tinyint,
|
||||
cql3_type::uuid,
|
||||
cql3_type::varchar,
|
||||
cql3_type::varint,
|
||||
cql3_type::timeuuid,
|
||||
cql3_type::date,
|
||||
@@ -371,6 +378,16 @@ cql3_type::values() {
|
||||
return v;
|
||||
}
|
||||
|
||||
shared_ptr<cql3_type>
|
||||
make_cql3_tuple_type(tuple_type t) {
|
||||
auto name = sprint("tuple<%s>",
|
||||
::join(", ",
|
||||
t->all_types()
|
||||
| boost::adaptors::transformed(std::mem_fn(&abstract_type::as_cql3_type))));
|
||||
return ::make_shared<cql3_type>(std::move(name), std::move(t), false);
|
||||
}
|
||||
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const cql3_type::raw& r) {
|
||||
return os << r.to_string();
|
||||
@@ -378,15 +395,18 @@ operator<<(std::ostream& os, const cql3_type::raw& r) {
|
||||
|
||||
namespace util {
|
||||
|
||||
sstring maybe_quote(const sstring& identifier) {
|
||||
static const std::regex unquoted_identifier_re("[a-z][a-z0-9_]*");
|
||||
if (std::regex_match(identifier.begin(), identifier.end(), unquoted_identifier_re)) {
|
||||
return identifier;
|
||||
sstring maybe_quote(const sstring& s) {
|
||||
static const std::regex unquoted("\\w*");
|
||||
static const std::regex double_quote("\"");
|
||||
|
||||
if (std::regex_match(s.begin(), s.end(), unquoted)) {
|
||||
return s;
|
||||
}
|
||||
static const std::regex double_quote_re("\"");
|
||||
std::string result = identifier;
|
||||
std::regex_replace(result, double_quote_re, "\"\"");
|
||||
return '"' + result + '"';
|
||||
std::ostringstream ss;
|
||||
ss << "\"";
|
||||
std::regex_replace(std::ostreambuf_iterator<char>(ss), s.begin(), s.end(), double_quote, "\"\"");
|
||||
ss << "\"";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,14 +54,17 @@ namespace cql3 {
|
||||
class ut_name;
|
||||
|
||||
class cql3_type final {
|
||||
sstring _name;
|
||||
data_type _type;
|
||||
bool _native;
|
||||
public:
|
||||
cql3_type(data_type type) : _type(std::move(type)) {}
|
||||
cql3_type(sstring name, data_type type, bool native = true)
|
||||
: _name(std::move(name)), _type(std::move(type)), _native(native) {}
|
||||
bool is_collection() const { return _type->is_collection(); }
|
||||
bool is_counter() const { return _type->is_counter(); }
|
||||
bool is_native() const { return _type->is_native(); }
|
||||
bool is_native() const { return _native; }
|
||||
data_type get_type() const { return _type; }
|
||||
const sstring& to_string() const { return _type->cql3_type_name(); }
|
||||
sstring to_string() const;
|
||||
|
||||
// For UserTypes, we need to know the current keyspace to resolve the
|
||||
// actual type used, so Raw is a "not yet prepared" CQL3Type.
|
||||
@@ -74,11 +77,11 @@ public:
|
||||
virtual bool is_counter() const;
|
||||
virtual bool is_duration() const;
|
||||
virtual bool references_user_type(const sstring&) const;
|
||||
virtual std::optional<sstring> keyspace() const;
|
||||
virtual std::experimental::optional<sstring> keyspace() const;
|
||||
virtual void freeze();
|
||||
virtual cql3_type prepare_internal(const sstring& keyspace, lw_shared_ptr<user_types_metadata>) = 0;
|
||||
virtual cql3_type prepare(database& db, const sstring& keyspace);
|
||||
static shared_ptr<raw> from(cql3_type type);
|
||||
virtual shared_ptr<cql3_type> prepare_internal(const sstring& keyspace, lw_shared_ptr<user_types_metadata>) = 0;
|
||||
virtual shared_ptr<cql3_type> prepare(database& db, const sstring& keyspace);
|
||||
static shared_ptr<raw> from(shared_ptr<cql3_type> type);
|
||||
static shared_ptr<raw> user_type(ut_name name);
|
||||
static shared_ptr<raw> map(shared_ptr<raw> t1, shared_ptr<raw> t2);
|
||||
static shared_ptr<raw> list(shared_ptr<raw> t);
|
||||
@@ -99,38 +102,74 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
static thread_local cql3_type ascii;
|
||||
static thread_local cql3_type bigint;
|
||||
static thread_local cql3_type blob;
|
||||
static thread_local cql3_type boolean;
|
||||
static thread_local cql3_type double_;
|
||||
static thread_local cql3_type empty;
|
||||
static thread_local cql3_type float_;
|
||||
static thread_local cql3_type int_;
|
||||
static thread_local cql3_type smallint;
|
||||
static thread_local cql3_type text;
|
||||
static thread_local cql3_type timestamp;
|
||||
static thread_local cql3_type tinyint;
|
||||
static thread_local cql3_type uuid;
|
||||
static thread_local cql3_type timeuuid;
|
||||
static thread_local cql3_type date;
|
||||
static thread_local cql3_type time;
|
||||
static thread_local cql3_type inet;
|
||||
static thread_local cql3_type varint;
|
||||
static thread_local cql3_type decimal;
|
||||
static thread_local cql3_type counter;
|
||||
static thread_local cql3_type duration;
|
||||
|
||||
static const std::vector<cql3_type>& values();
|
||||
enum class kind : int8_t {
|
||||
ASCII, BIGINT, BLOB, BOOLEAN, COUNTER, DECIMAL, DOUBLE, EMPTY, FLOAT, INT, SMALLINT, TINYINT, INET, TEXT, TIMESTAMP, UUID, VARCHAR, VARINT, TIMEUUID, DATE, TIME, DURATION
|
||||
};
|
||||
using kind_enum = super_enum<kind,
|
||||
kind::ASCII,
|
||||
kind::BIGINT,
|
||||
kind::BLOB,
|
||||
kind::BOOLEAN,
|
||||
kind::COUNTER,
|
||||
kind::DECIMAL,
|
||||
kind::DOUBLE,
|
||||
kind::EMPTY,
|
||||
kind::FLOAT,
|
||||
kind::INET,
|
||||
kind::INT,
|
||||
kind::SMALLINT,
|
||||
kind::TINYINT,
|
||||
kind::TEXT,
|
||||
kind::TIMESTAMP,
|
||||
kind::UUID,
|
||||
kind::VARCHAR,
|
||||
kind::VARINT,
|
||||
kind::TIMEUUID,
|
||||
kind::DATE,
|
||||
kind::TIME,
|
||||
kind::DURATION>;
|
||||
using kind_enum_set = enum_set<kind_enum>;
|
||||
private:
|
||||
std::experimental::optional<kind_enum_set::prepared> _kind;
|
||||
static shared_ptr<cql3_type> make(sstring name, data_type type, kind kind_) {
|
||||
return make_shared<cql3_type>(std::move(name), std::move(type), kind_);
|
||||
}
|
||||
public:
|
||||
using kind = abstract_type::cql3_kind;
|
||||
using kind_enum_set = abstract_type::cql3_kind_enum_set;
|
||||
kind_enum_set::prepared get_kind() const { return _type->get_cql3_kind(); }
|
||||
static thread_local shared_ptr<cql3_type> ascii;
|
||||
static thread_local shared_ptr<cql3_type> bigint;
|
||||
static thread_local shared_ptr<cql3_type> blob;
|
||||
static thread_local shared_ptr<cql3_type> boolean;
|
||||
static thread_local shared_ptr<cql3_type> double_;
|
||||
static thread_local shared_ptr<cql3_type> empty;
|
||||
static thread_local shared_ptr<cql3_type> float_;
|
||||
static thread_local shared_ptr<cql3_type> int_;
|
||||
static thread_local shared_ptr<cql3_type> smallint;
|
||||
static thread_local shared_ptr<cql3_type> text;
|
||||
static thread_local shared_ptr<cql3_type> timestamp;
|
||||
static thread_local shared_ptr<cql3_type> tinyint;
|
||||
static thread_local shared_ptr<cql3_type> uuid;
|
||||
static thread_local shared_ptr<cql3_type> varchar;
|
||||
static thread_local shared_ptr<cql3_type> timeuuid;
|
||||
static thread_local shared_ptr<cql3_type> date;
|
||||
static thread_local shared_ptr<cql3_type> time;
|
||||
static thread_local shared_ptr<cql3_type> inet;
|
||||
static thread_local shared_ptr<cql3_type> varint;
|
||||
static thread_local shared_ptr<cql3_type> decimal;
|
||||
static thread_local shared_ptr<cql3_type> counter;
|
||||
static thread_local shared_ptr<cql3_type> duration;
|
||||
|
||||
static const std::vector<shared_ptr<cql3_type>>& values();
|
||||
public:
|
||||
cql3_type(sstring name, data_type type, kind kind_)
|
||||
: _name(std::move(name)), _type(std::move(type)), _native(true), _kind(kind_enum_set::prepare(kind_)) {
|
||||
}
|
||||
kind_enum_set::prepared get_kind() const {
|
||||
assert(_kind);
|
||||
return *_kind;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const cql3_type& a, const cql3_type& b) {
|
||||
return a.get_type() == b.get_type();
|
||||
}
|
||||
shared_ptr<cql3_type> make_cql3_tuple_type(tuple_type t);
|
||||
|
||||
#if 0
|
||||
public static class Custom implements CQL3Type
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
#include "service/query_state.hh"
|
||||
#include "service/storage_proxy.hh"
|
||||
#include "cql3/query_options.hh"
|
||||
#include "timeout_config.hh"
|
||||
|
||||
namespace cql_transport {
|
||||
|
||||
@@ -63,15 +62,10 @@ class metadata;
|
||||
shared_ptr<const metadata> make_empty_metadata();
|
||||
|
||||
class cql_statement {
|
||||
timeout_config_selector _timeout_config_selector;
|
||||
public:
|
||||
explicit cql_statement(timeout_config_selector timeout_selector) : _timeout_config_selector(timeout_selector) {}
|
||||
|
||||
virtual ~cql_statement()
|
||||
{ }
|
||||
|
||||
timeout_config_selector get_timeout_config_selector() const { return _timeout_config_selector; }
|
||||
|
||||
virtual uint32_t get_bound_terms() = 0;
|
||||
|
||||
/**
|
||||
@@ -87,7 +81,7 @@ public:
|
||||
*
|
||||
* @param state the current client state
|
||||
*/
|
||||
virtual void validate(service::storage_proxy& proxy, const service::client_state& state) = 0;
|
||||
virtual void validate(distributed<service::storage_proxy>& proxy, const service::client_state& state) = 0;
|
||||
|
||||
/**
|
||||
* Execute the statement and return the resulting result or null if there is no result.
|
||||
@@ -96,7 +90,15 @@ public:
|
||||
* @param options options for this query (consistency, variables, pageSize, ...)
|
||||
*/
|
||||
virtual future<::shared_ptr<cql_transport::messages::result_message>>
|
||||
execute(service::storage_proxy& proxy, service::query_state& state, const query_options& options) = 0;
|
||||
execute(distributed<service::storage_proxy>& proxy, service::query_state& state, const query_options& options) = 0;
|
||||
|
||||
/**
|
||||
* Variant of execute used for internal query against the system tables, and thus only query the local node = 0.
|
||||
*
|
||||
* @param state the current query state
|
||||
*/
|
||||
virtual future<::shared_ptr<cql_transport::messages::result_message>>
|
||||
execute_internal(distributed<service::storage_proxy>& proxy, service::query_state& state, const query_options& options) = 0;
|
||||
|
||||
virtual bool uses_function(const sstring& ks_name, const sstring& function_name) const = 0;
|
||||
|
||||
@@ -109,7 +111,6 @@ public:
|
||||
|
||||
class cql_statement_no_metadata : public cql_statement {
|
||||
public:
|
||||
using cql_statement::cql_statement;
|
||||
virtual shared_ptr<const metadata> get_result_metadata() const override {
|
||||
return make_empty_metadata();
|
||||
}
|
||||
|
||||
@@ -41,9 +41,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "function.hh"
|
||||
#include "types.hh"
|
||||
#include "cql3/cql3_type.hh"
|
||||
#include <vector>
|
||||
#include <iosfwd>
|
||||
#include <boost/functional/hash.hpp>
|
||||
@@ -74,7 +72,7 @@ public:
|
||||
return _arg_types;
|
||||
}
|
||||
|
||||
virtual const data_type& return_type() const {
|
||||
virtual data_type return_type() const {
|
||||
return _return_type;
|
||||
}
|
||||
|
||||
@@ -93,7 +91,7 @@ public:
|
||||
}
|
||||
|
||||
virtual sstring column_name(const std::vector<sstring>& column_names) override {
|
||||
return format("{}({})", _name, join(", ", column_names));
|
||||
return sprint("%s(%s)", _name, join(", ", column_names));
|
||||
}
|
||||
|
||||
virtual void print(std::ostream& os) const override;
|
||||
@@ -107,9 +105,9 @@ abstract_function::print(std::ostream& os) const {
|
||||
if (i > 0) {
|
||||
os << ", ";
|
||||
}
|
||||
os << _arg_types[i]->as_cql3_type().to_string();
|
||||
os << _arg_types[i]->name(); // FIXME: asCQL3Type()
|
||||
}
|
||||
os << ") -> " << _return_type->as_cql3_type().to_string();
|
||||
os << ") -> " << _return_type->name(); // FIXME: asCQL3Type()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user