Compare commits
174 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b252bba4a2 | ||
|
|
a0b9fcc041 | ||
|
|
35c9b675c1 | ||
|
|
d71836fef7 | ||
|
|
f8e150e97c | ||
|
|
10c300f894 | ||
|
|
de1d3e5c6b | ||
|
|
69810c13ca | ||
|
|
9b025a5742 | ||
|
|
74eebc4cab | ||
|
|
9b2ca4ee44 | ||
|
|
773bf45774 | ||
|
|
c6705b4335 | ||
|
|
3997871b4d | ||
|
|
4ff1d731bd | ||
|
|
0e0f9143c9 | ||
|
|
9d809d6ea4 | ||
|
|
630d599c34 | ||
|
|
0933c1a00a | ||
|
|
7a7099fcfb | ||
|
|
50235aacb4 | ||
|
|
e888009f12 | ||
|
|
a19615ee9b | ||
|
|
357ca67fda | ||
|
|
7818c63eb1 | ||
|
|
da10eae18c | ||
|
|
d5292cd3ec | ||
|
|
9cb35361d9 | ||
|
|
3e285248be | ||
|
|
6f10ccb441 | ||
|
|
df420499bc | ||
|
|
d29527b4e1 | ||
|
|
8a90e242e4 | ||
|
|
8a78c0aba9 | ||
|
|
8a2bbcf138 | ||
|
|
22c891e6df | ||
|
|
1841d0c2d9 | ||
|
|
e10107fe5a | ||
|
|
0b3a4679db | ||
|
|
ba60d666a9 | ||
|
|
6ea4d0b75c | ||
|
|
8c5911f312 | ||
|
|
de00d7f5a1 | ||
|
|
e5f9dae4bb | ||
|
|
e13e796290 | ||
|
|
336c771663 | ||
|
|
82968afc25 | ||
|
|
383dcffb53 | ||
|
|
0c2abc007c | ||
|
|
1498c4f150 | ||
|
|
f388992a94 | ||
|
|
310540c11f | ||
|
|
7d833023cc | ||
|
|
d94ac196e0 | ||
|
|
1d7430995e | ||
|
|
b662a7f8a4 | ||
|
|
447ad72882 | ||
|
|
b8485d3bce | ||
|
|
034b0f50db | ||
|
|
12ec0becf3 | ||
|
|
666b19552d | ||
|
|
178f870a03 | ||
|
|
1b18f16dc1 | ||
|
|
28934575e4 | ||
|
|
182cbeefb0 | ||
|
|
b70fc41a90 | ||
|
|
debfc795b2 | ||
|
|
0d094575ec | ||
|
|
20baef69a9 | ||
|
|
1bac88601d | ||
|
|
e581fd1463 | ||
|
|
b366bff998 | ||
|
|
38e6984ba5 | ||
|
|
332f76579e | ||
|
|
315a03cf6c | ||
|
|
1847dc7a6a | ||
|
|
dd11b5987e | ||
|
|
a134e8699a | ||
|
|
bd7dcbb8d2 | ||
|
|
74e61528a6 | ||
|
|
5eb4fde2d5 | ||
|
|
cc0703f8ca | ||
|
|
678283a5bb | ||
|
|
552c0d7641 | ||
|
|
860c06660b | ||
|
|
db733ba075 | ||
|
|
88677d39c8 | ||
|
|
d767dee5ec | ||
|
|
702f6ee1b7 | ||
|
|
473b9aec65 | ||
|
|
b548061257 | ||
|
|
01165a9ae7 | ||
|
|
5cdb963768 | ||
|
|
7c9b9a4e24 | ||
|
|
f475c65ae6 | ||
|
|
687372bc48 | ||
|
|
65c140121c | ||
|
|
ed68ad220f | ||
|
|
35f4b8fbbe | ||
|
|
48012fe418 | ||
|
|
c862ccda91 | ||
|
|
83b1057c4b | ||
|
|
c1cb779dd2 | ||
|
|
b47d18f9fd | ||
|
|
f8713b019e | ||
|
|
cd5e4eace5 | ||
|
|
4fb5403670 | ||
|
|
e9df6c42ce | ||
|
|
5fdf492ccc | ||
|
|
fd2b02a12c | ||
|
|
f8cec2f891 | ||
|
|
e4d6577ef2 | ||
|
|
346027248d | ||
|
|
2cf6191353 | ||
|
|
b52d647de2 | ||
|
|
f7c96a37f1 | ||
|
|
ae71ffdcfd | ||
|
|
a235900388 | ||
|
|
be9f150341 | ||
|
|
2478fa1f6e | ||
|
|
d95ac1826e | ||
|
|
6fc17345e9 | ||
|
|
4bfa0ae247 | ||
|
|
174b7870e6 | ||
|
|
e95b4ee825 | ||
|
|
464305de1c | ||
|
|
3a1a9e1a11 | ||
|
|
90dac5d944 | ||
|
|
e5a83d105c | ||
|
|
9b4a0a2879 | ||
|
|
adad12ddc3 | ||
|
|
a77bb1fe34 | ||
|
|
3c7e6dfdb9 | ||
|
|
fab136ae1d | ||
|
|
a4218f536b | ||
|
|
9f4431ef04 | ||
|
|
66250bf8cc | ||
|
|
88fe3c2694 | ||
|
|
db4c3d3e52 | ||
|
|
ca22a1cd1a | ||
|
|
f9b702764e | ||
|
|
54701bd95c | ||
|
|
30eca5f534 | ||
|
|
cd057d3882 | ||
|
|
c5a5a2265e | ||
|
|
3e482c6c9d | ||
|
|
5b6cadb890 | ||
|
|
9cf8cd6c02 | ||
|
|
b34567b69b | ||
|
|
02b763ed97 | ||
|
|
05500a52d7 | ||
|
|
4afa558e97 | ||
|
|
f3956421f7 | ||
|
|
a17a6ce8f5 | ||
|
|
58a362c1f2 | ||
|
|
361b2dd7a5 | ||
|
|
f6a2bafae2 | ||
|
|
2ec25a55cd | ||
|
|
d3fb7c5515 | ||
|
|
b1ac6a36f2 | ||
|
|
8cba125bce | ||
|
|
f46f9f7533 | ||
|
|
090d991f8e | ||
|
|
ae15a80d01 | ||
|
|
6cf902343a | ||
|
|
d5e59f671c | ||
|
|
38944655c5 | ||
|
|
06e274ff34 | ||
|
|
c24d4a8acb | ||
|
|
5f95b76c65 | ||
|
|
0bdb7e1e7c | ||
|
|
56ea4f3154 | ||
|
|
d9c178063c | ||
|
|
b21b7f73b9 |
@@ -1,3 +0,0 @@
|
||||
.git
|
||||
build
|
||||
seastar/build
|
||||
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.3.6
|
||||
|
||||
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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
api/api.cc
10
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,13 +36,11 @@
|
||||
#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 {
|
||||
|
||||
static std::unique_ptr<reply> exception_reply(std::exception_ptr eptr) {
|
||||
|
||||
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;
|
||||
},
|
||||
@@ -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) {
|
||||
@@ -936,45 +920,5 @@ void set_column_family(http_context& ctx, routes& r) {
|
||||
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,7 +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 {
|
||||
@@ -72,13 +71,12 @@ 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;
|
||||
});
|
||||
std::any operator()(database& db) const {
|
||||
auto res = init;
|
||||
for (auto i : db.get_column_families()) {
|
||||
res = reducer(res, mapper(*i.second.get()));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#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>
|
||||
|
||||
|
||||
@@ -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: "));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "atomic_cell.hh"
|
||||
#include "atomic_cell_or_collection.hh"
|
||||
#include "types.hh"
|
||||
#include "types/collection.hh"
|
||||
|
||||
/// LSA mirator for cells with irrelevant type
|
||||
///
|
||||
@@ -48,23 +47,6 @@ atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_typ
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -74,25 +56,6 @@ atomic_cell atomic_cell::make_live(const abstract_type& type, api::timestamp_typ
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
@@ -192,20 +155,20 @@ bool atomic_cell_or_collection::equals(const abstract_type& type, const atomic_c
|
||||
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()) {
|
||||
if (!b.is_live()) {
|
||||
return false;
|
||||
}
|
||||
if (a.is_counter_update()) {
|
||||
if (!b.is_counter_update()) {
|
||||
return false;
|
||||
}
|
||||
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 (!b.is_live_and_has_ttl()) {
|
||||
return false;
|
||||
}
|
||||
if (a.ttl() != b.ttl() || a.expiry() != b.expiry()) {
|
||||
return false;
|
||||
}
|
||||
@@ -244,18 +207,16 @@ size_t atomic_cell_or_collection::external_memory_usage(const abstract_type& t)
|
||||
+ 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()) {
|
||||
std::ostream& operator<<(std::ostream& os, const atomic_cell_or_collection& c) {
|
||||
if (!c._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());
|
||||
if (dc::structure::get_member<dc::tags::flags>(c._data.get()).get<dc::tags::collection>()) {
|
||||
os << "collection";
|
||||
} else {
|
||||
os << p._cell.as_atomic_cell(p._cdef);
|
||||
os << "atomic cell";
|
||||
}
|
||||
return os << " }";
|
||||
return os << " @" << static_cast<const void*>(c._data.get()) << " }";
|
||||
}
|
||||
|
||||
@@ -26,16 +26,13 @@
|
||||
#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"
|
||||
|
||||
class abstract_type;
|
||||
class collection_type_impl;
|
||||
@@ -189,10 +186,6 @@ public:
|
||||
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);
|
||||
@@ -200,10 +193,6 @@ public:
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -67,19 +67,7 @@ public:
|
||||
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&);
|
||||
friend std::ostream& operator<<(std::ostream&, const atomic_cell_or_collection&);
|
||||
};
|
||||
|
||||
namespace std {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -48,9 +48,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 +60,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,29 +78,20 @@ 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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -76,12 +76,12 @@ 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&);
|
||||
future<> wait_for_schema_agreement(::service::migration_manager&, const database&);
|
||||
|
||||
///
|
||||
/// Time-outs for internal, non-local CQL queries.
|
||||
|
||||
@@ -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,11 +94,11 @@ 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,
|
||||
@@ -113,7 +112,7 @@ 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,
|
||||
@@ -161,7 +160,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 +178,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 +187,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,
|
||||
@@ -210,12 +210,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,
|
||||
@@ -233,16 +234,17 @@ default_authorizer::modify(
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
@@ -270,8 +272,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);
|
||||
@@ -290,7 +293,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,
|
||||
@@ -307,7 +311,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,
|
||||
|
||||
@@ -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<>()) {
|
||||
}
|
||||
|
||||
// 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 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 !row.get_or<sstring>(SALTED_HASH, "").empty();
|
||||
}
|
||||
|
||||
static const sstring update_row_query = format("UPDATE {} SET {} = ? WHERE {} = ?",
|
||||
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,12 +176,12 @@ 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,
|
||||
@@ -140,7 +212,7 @@ future<> password_authenticator::create_default_if_missing() const {
|
||||
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 +223,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 +233,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 +258,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 +287,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 +302,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);
|
||||
@@ -242,11 +317,11 @@ future<authenticated_user> password_authenticator::authenticate(
|
||||
}).then_wrapped([=](future<::shared_ptr<cql3::untyped_result_set>> f) {
|
||||
try {
|
||||
auto res = f.get0();
|
||||
auto salted_hash = std::optional<sstring>();
|
||||
auto salted_hash = std::experimental::optional<sstring>();
|
||||
if (!res->empty()) {
|
||||
salted_hash = res->one().get_opt<sstring>(SALTED_HASH);
|
||||
}
|
||||
if (!salted_hash || !passwords::check(password, *salted_hash)) {
|
||||
if (!salted_hash || !checkpw(password, *salted_hash)) {
|
||||
throw exceptions::authentication_exception("Username and/or password are incorrect");
|
||||
}
|
||||
return make_ready_future<authenticated_user>(username);
|
||||
@@ -254,15 +329,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<>();
|
||||
}
|
||||
@@ -271,15 +344,16 @@ future<> password_authenticator::create(std::string_view role_name, const authen
|
||||
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);
|
||||
@@ -288,11 +362,12 @@ future<> password_authenticator::alter(std::string_view role_name, const authent
|
||||
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);
|
||||
@@ -303,7 +378,7 @@ future<> password_authenticator::drop(std::string_view name) const {
|
||||
{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 +387,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);
|
||||
|
||||
@@ -97,7 +98,7 @@ 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(
|
||||
|
||||
@@ -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/config.hh"
|
||||
#include "db/consistency_level_type.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] {
|
||||
@@ -194,16 +206,18 @@ future<> service::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);
|
||||
|
||||
@@ -246,7 +260,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 +274,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 +291,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 +309,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 +320,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 +415,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 +439,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 +456,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 +472,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 +489,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 +497,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 +510,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 +521,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,8 +80,9 @@ 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);
|
||||
|
||||
@@ -93,12 +93,12 @@ static future<std::optional<record>> find_record(cql3::query_processor& qp, std:
|
||||
{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 +109,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 +123,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,7 +166,8 @@ 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);
|
||||
|
||||
@@ -190,12 +191,12 @@ 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,
|
||||
@@ -226,7 +227,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,11 +251,12 @@ 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);
|
||||
|
||||
@@ -267,7 +269,7 @@ future<> standard_role_manager::create_or_replace(std::string_view role_name, co
|
||||
}
|
||||
|
||||
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 +280,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,7 +301,8 @@ 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),
|
||||
@@ -309,7 +312,7 @@ standard_role_manager::alter(std::string_view role_name, const role_config_updat
|
||||
});
|
||||
}
|
||||
|
||||
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,7 +320,8 @@ 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(
|
||||
@@ -355,7 +359,8 @@ 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);
|
||||
|
||||
@@ -374,14 +379,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);
|
||||
@@ -397,7 +402,8 @@ 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(),
|
||||
@@ -405,7 +411,8 @@ standard_role_manager::modify_membership(
|
||||
|
||||
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(),
|
||||
@@ -419,7 +426,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 +457,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 +479,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 +497,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,7 +508,8 @@ 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());
|
||||
|
||||
@@ -526,19 +534,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<>())
|
||||
{
|
||||
@@ -125,7 +125,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, 10}, {soft_limit + (hard_dirty_limit - soft_limit) / 2, 200} , {hard_dirty_limit, 1000}}),
|
||||
std::move(current_dirty)
|
||||
)
|
||||
{}
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
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;
|
||||
}
|
||||
|
||||
14
bytes.hh
14
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_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 {
|
||||
|
||||
|
||||
@@ -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,12 +57,12 @@ 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;
|
||||
@@ -102,13 +102,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 +116,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 +140,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 +162,6 @@ public:
|
||||
: _begin()
|
||||
, _current(nullptr)
|
||||
, _size(0)
|
||||
, _initial_chunk_size(o._initial_chunk_size)
|
||||
{
|
||||
append(o);
|
||||
}
|
||||
@@ -209,20 +199,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 +219,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));
|
||||
}
|
||||
@@ -406,19 +393,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;
|
||||
@@ -100,13 +99,7 @@ class cache_flat_mutation_reader final : public flat_mutation_reader::impl {
|
||||
// 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 +137,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 +157,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 +189,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 +235,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);
|
||||
});
|
||||
}
|
||||
@@ -308,7 +283,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();
|
||||
@@ -453,7 +428,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);
|
||||
@@ -605,7 +580,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,7 +602,7 @@ 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));
|
||||
@@ -668,7 +643,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 +678,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);
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
#include <boost/intrusive/unordered_set.hpp>
|
||||
|
||||
#include "utils/small_vector.hh"
|
||||
#include "fnv1a_hasher.hh"
|
||||
#include "mutation_fragment.hh"
|
||||
#include "mutation_partition.hh"
|
||||
#include "xx_hasher.hh"
|
||||
|
||||
#include "db/timeout_clock.hh"
|
||||
|
||||
@@ -63,7 +63,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 +194,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 {
|
||||
|
||||
@@ -61,8 +61,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 +73,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 +111,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);
|
||||
|
||||
@@ -284,7 +284,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 +295,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);
|
||||
}
|
||||
@@ -346,7 +346,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 +468,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 +511,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;
|
||||
|
||||
24
compress.cc
24
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,7 +241,7 @@ size_t lz4_processor::compress(const char* input, size_t input_len,
|
||||
output[1] = (input_len >> 8) & 0xFF;
|
||||
output[2] = (input_len >> 16) & 0xFF;
|
||||
output[3] = (input_len >> 24) & 0xFF;
|
||||
#ifdef HAVE_LZ4_COMPRESS_DEFAULT
|
||||
#ifdef SEASTAR_HAVE_LZ4_COMPRESS_DEFAULT
|
||||
auto ret = LZ4_compress_default(input, output + 4, input_len, LZ4_compressBound(input_len));
|
||||
#else
|
||||
auto ret = LZ4_compress(input, output + 4, input_len);
|
||||
|
||||
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
|
||||
|
||||
1076
configure.py
1076
configure.py
File diff suppressed because it is too large
Load Diff
@@ -38,44 +38,44 @@ 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;
|
||||
}
|
||||
dst.apply(new_def, upgrade_cell(*new_def.type, *old_type, cell));
|
||||
auto new_cell = [&] {
|
||||
if (cell.is_live() && !old_type->is_counter()) {
|
||||
if (cell.is_live_and_has_ttl()) {
|
||||
return atomic_cell_or_collection(
|
||||
atomic_cell::make_live(*new_def.type, cell.timestamp(), cell.value().linearize(), cell.expiry(), cell.ttl())
|
||||
);
|
||||
}
|
||||
return atomic_cell_or_collection(
|
||||
atomic_cell::make_live(*new_def.type, cell.timestamp(), cell.value().linearize())
|
||||
);
|
||||
} else {
|
||||
return atomic_cell_or_collection(*new_def.type, cell);
|
||||
}
|
||||
}();
|
||||
dst.apply(new_def, std::move(new_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_bv);
|
||||
|
||||
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:
|
||||
@@ -92,10 +92,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 +119,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());
|
||||
|
||||
@@ -171,7 +171,7 @@ void counter_cell_view::apply(const column_definition& cdef, atomic_cell_or_coll
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<atomic_cell> counter_cell_view::difference(atomic_cell_view a, atomic_cell_view b)
|
||||
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());
|
||||
@@ -204,7 +204,7 @@ 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()) {
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "atomic_cell_or_collection.hh"
|
||||
#include "types.hh"
|
||||
|
||||
#include "stdx.hh"
|
||||
|
||||
class mutation;
|
||||
|
||||
class mutation;
|
||||
@@ -383,7 +385,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 +395,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());
|
||||
}
|
||||
@@ -422,7 +424,7 @@ struct counter_cell_view : basic_counter_cell_view<mutable_view::no> {
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
168
cql3/Cql.g
168
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));
|
||||
@@ -381,11 +382,9 @@ 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; } )?
|
||||
@@ -395,14 +394,12 @@ selectStatement returns [shared_ptr<raw::select_statement> expr]
|
||||
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, is_json);
|
||||
$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));
|
||||
}
|
||||
;
|
||||
|
||||
@@ -473,7 +470,6 @@ insertStatement returns [::shared_ptr<raw::modification_statement> expr]
|
||||
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
|
||||
@@ -491,15 +487,13 @@ insertStatement returns [::shared_ptr<raw::modification_statement> expr]
|
||||
}
|
||||
| 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);
|
||||
if_not_exists);
|
||||
}
|
||||
)
|
||||
;
|
||||
@@ -823,15 +817,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 +894,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 +903,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 +1531,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 +1558,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 +1625,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]
|
||||
@@ -1726,10 +1672,6 @@ basic_unreserved_keyword returns [sstring str]
|
||||
| K_NON
|
||||
| K_DETERMINISTIC
|
||||
| K_JSON
|
||||
| K_CACHE
|
||||
| K_BYPASS
|
||||
| K_PER
|
||||
| K_PARTITION
|
||||
) { $str = $k.text; }
|
||||
;
|
||||
|
||||
@@ -1867,16 +1809,6 @@ 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 {
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -125,13 +125,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
@@ -225,9 +225,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 +240,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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "function.hh"
|
||||
#include "types.hh"
|
||||
#include "cql3/cql3_type.hh"
|
||||
#include <vector>
|
||||
@@ -74,7 +73,7 @@ public:
|
||||
return _arg_types;
|
||||
}
|
||||
|
||||
virtual const data_type& return_type() const {
|
||||
virtual data_type return_type() const {
|
||||
return _return_type;
|
||||
}
|
||||
|
||||
@@ -93,7 +92,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 +106,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]->as_cql3_type()->to_string();
|
||||
}
|
||||
os << ") -> " << _return_type->as_cql3_type().to_string();
|
||||
os << ") -> " << _return_type->as_cql3_type()->to_string();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ struct aggregate_type_for<timeuuid_native_type> {
|
||||
|
||||
template <typename Type>
|
||||
class impl_max_function_for final : public aggregate_function::aggregate {
|
||||
std::optional<typename aggregate_type_for<Type>::type> _max{};
|
||||
std::experimental::optional<typename aggregate_type_for<Type>::type> _max{};
|
||||
public:
|
||||
virtual void reset() override {
|
||||
_max = {};
|
||||
@@ -258,7 +258,7 @@ public:
|
||||
if (!_max) {
|
||||
return {};
|
||||
}
|
||||
return data_type_for<Type>()->decompose(data_value(Type{*_max}));
|
||||
return data_type_for<Type>()->decompose(Type{*_max});
|
||||
}
|
||||
virtual void add_input(cql_serialization_format sf, const std::vector<opt_bytes>& values) override {
|
||||
if (!values[0]) {
|
||||
@@ -296,7 +296,7 @@ make_max_function() {
|
||||
|
||||
template <typename Type>
|
||||
class impl_min_function_for final : public aggregate_function::aggregate {
|
||||
std::optional<typename aggregate_type_for<Type>::type> _min{};
|
||||
std::experimental::optional<typename aggregate_type_for<Type>::type> _min{};
|
||||
public:
|
||||
virtual void reset() override {
|
||||
_min = {};
|
||||
@@ -305,7 +305,7 @@ public:
|
||||
if (!_min) {
|
||||
return {};
|
||||
}
|
||||
return data_type_for<Type>()->decompose(data_value(Type{*_min}));
|
||||
return data_type_for<Type>()->decompose(Type{*_min});
|
||||
}
|
||||
virtual void add_input(cql_serialization_format sf, const std::vector<opt_bytes>& values) override {
|
||||
if (!values[0]) {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "function.hh"
|
||||
#include <optional>
|
||||
#include <experimental/optional>
|
||||
|
||||
namespace cql3 {
|
||||
namespace functions {
|
||||
|
||||
@@ -1,156 +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) 2018 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 "cql3/functions/function.hh"
|
||||
#include "cql3/functions/scalar_function.hh"
|
||||
#include "cql3/cql3_type.hh"
|
||||
|
||||
#include "bytes_ostream.hh"
|
||||
#include "types.hh"
|
||||
|
||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||
|
||||
namespace cql3 {
|
||||
|
||||
namespace functions {
|
||||
|
||||
/*
|
||||
* This function is used for handling 'SELECT JSON' statement.
|
||||
* 'SELECT JSON' is supposed to return a single column named '[json]'
|
||||
* with JSON representation of the query result as its value.
|
||||
* In order to achieve it, selectors from 'SELECT' are wrapped with
|
||||
* 'as_json' function, which also keeps information about underlying
|
||||
* selector names and types. This function is not registered in functions.cc,
|
||||
* because it should not be invoked directly from CQL.
|
||||
* Case-sensitive column names are wrapped in additional quotes,
|
||||
* as stated in CQL-JSON documentation.
|
||||
*/
|
||||
class as_json_function : public scalar_function {
|
||||
std::vector<sstring> _selector_names;
|
||||
std::vector<data_type> _selector_types;
|
||||
public:
|
||||
as_json_function(std::vector<sstring>&& selector_names, std::vector<data_type> selector_types)
|
||||
: _selector_names(std::move(selector_names)), _selector_types(std::move(selector_types)) {
|
||||
}
|
||||
|
||||
virtual bytes_opt execute(cql_serialization_format sf, const std::vector<bytes_opt>& parameters) override {
|
||||
bytes_ostream encoded_row;
|
||||
encoded_row.write("{", 1);
|
||||
for (size_t i = 0; i < _selector_names.size(); ++i) {
|
||||
if (i > 0) {
|
||||
encoded_row.write(", ", 2);
|
||||
}
|
||||
bool has_any_upper = boost::algorithm::any_of(_selector_names[i], [](unsigned char c) { return std::isupper(c); });
|
||||
encoded_row.write("\"", 1);
|
||||
if (has_any_upper) {
|
||||
encoded_row.write("\\\"", 2);
|
||||
}
|
||||
encoded_row.write(_selector_names[i].c_str(), _selector_names[i].size());
|
||||
if (has_any_upper) {
|
||||
encoded_row.write("\\\"", 2);
|
||||
}
|
||||
encoded_row.write("\": ", 3);
|
||||
sstring row_sstring = _selector_types[i]->to_json_string(parameters[i]);
|
||||
encoded_row.write(row_sstring.c_str(), row_sstring.size());
|
||||
}
|
||||
encoded_row.write("}", 1);
|
||||
return bytes(encoded_row.linearize());
|
||||
}
|
||||
|
||||
virtual const function_name& name() const override {
|
||||
static const function_name f_name = function_name::native_function("as_json");
|
||||
return f_name;
|
||||
}
|
||||
|
||||
virtual const std::vector<data_type>& arg_types() const override {
|
||||
return _selector_types;
|
||||
}
|
||||
|
||||
virtual const data_type& return_type() const override {
|
||||
return utf8_type;
|
||||
}
|
||||
|
||||
virtual bool is_pure() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool is_native() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool is_aggregate() override {
|
||||
// Aggregates of aggregates are currently not supported, but JSON handles them
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void print(std::ostream& os) const override {
|
||||
os << "as_json(";
|
||||
bool first = true;
|
||||
for (const sstring& selector_name: _selector_names) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
os << ", ";
|
||||
}
|
||||
os << selector_name;
|
||||
}
|
||||
os << ") -> " << utf8_type->as_cql3_type().to_string();
|
||||
}
|
||||
|
||||
virtual bool uses_function(const sstring& ks_name, const sstring& function_name) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool has_reference_to(function& f) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual sstring column_name(const std::vector<sstring>& column_names) override {
|
||||
return "[json]";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
#include "native_scalar_function.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include <seastar/core/print.hh>
|
||||
#include "core/print.hh"
|
||||
#include "cql3/cql3_type.hh"
|
||||
|
||||
namespace cql3 {
|
||||
@@ -56,7 +56,7 @@ namespace functions {
|
||||
inline
|
||||
shared_ptr<function>
|
||||
make_to_blob_function(data_type from_type) {
|
||||
auto name = from_type->as_cql3_type().to_string() + "asblob";
|
||||
auto name = from_type->as_cql3_type()->to_string() + "asblob";
|
||||
return make_native_scalar_function<true>(name, bytes_type, { from_type },
|
||||
[] (cql_serialization_format sf, const std::vector<bytes_opt>& parameters) {
|
||||
return parameters[0];
|
||||
@@ -66,7 +66,7 @@ make_to_blob_function(data_type from_type) {
|
||||
inline
|
||||
shared_ptr<function>
|
||||
make_from_blob_function(data_type to_type) {
|
||||
sstring name = sstring("blobas") + to_type->as_cql3_type().to_string();
|
||||
sstring name = sstring("blobas") + to_type->as_cql3_type()->to_string();
|
||||
return make_native_scalar_function<true>(name, to_type, { bytes_type },
|
||||
[name, to_type] (cql_serialization_format sf, const std::vector<bytes_opt>& parameters) -> bytes_opt {
|
||||
auto&& val = parameters[0];
|
||||
@@ -74,12 +74,13 @@ make_from_blob_function(data_type to_type) {
|
||||
return val;
|
||||
}
|
||||
try {
|
||||
to_type->validate(*val, sf);
|
||||
to_type->validate(*val);
|
||||
return val;
|
||||
} catch (marshal_exception& e) {
|
||||
using namespace exceptions;
|
||||
throw invalid_request_exception(format("In call to function {}, value 0x{} is not a valid binary representation for type {}",
|
||||
name, to_hex(val), to_type->as_cql3_type().to_string()));
|
||||
throw invalid_request_exception(sprint(
|
||||
"In call to function %s, value 0x%s is not a valid binary representation for type %s",
|
||||
name, to_hex(val), to_type->as_cql3_type()->to_string()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace functions {
|
||||
|
||||
namespace {
|
||||
|
||||
using bytes_opt = std::optional<bytes>;
|
||||
using bytes_opt = std::experimental::optional<bytes>;
|
||||
|
||||
class castas_function_for : public cql3::functions::native_scalar_function {
|
||||
castas_fctn _func;
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
castas_function_for(data_type to_type,
|
||||
data_type from_type,
|
||||
castas_fctn func)
|
||||
: native_scalar_function("castas" + to_type->as_cql3_type().to_string(), to_type, {from_type})
|
||||
: native_scalar_function("castas" + to_type->as_cql3_type()->to_string(), to_type, {from_type})
|
||||
, _func(func) {
|
||||
}
|
||||
virtual bool is_pure() override {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
#include "cql3/functions/function.hh"
|
||||
#include "cql3/functions/abstract_function.hh"
|
||||
#include "exceptions/exceptions.hh"
|
||||
#include <seastar/core/print.hh>
|
||||
#include "core/print.hh"
|
||||
#include "cql3/cql3_type.hh"
|
||||
#include "cql3/selection/selector.hh"
|
||||
|
||||
|
||||
@@ -44,18 +44,18 @@
|
||||
#include "function_name.hh"
|
||||
#include "types.hh"
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <experimental/optional>
|
||||
|
||||
namespace cql3 {
|
||||
namespace functions {
|
||||
|
||||
class function {
|
||||
public:
|
||||
using opt_bytes = std::optional<bytes>;
|
||||
using opt_bytes = std::experimental::optional<bytes>;
|
||||
virtual ~function() {}
|
||||
virtual const function_name& name() const = 0;
|
||||
virtual const std::vector<data_type>& arg_types() const = 0;
|
||||
virtual const data_type& return_type() const = 0;
|
||||
virtual data_type return_type() const = 0;
|
||||
|
||||
/**
|
||||
* Checks whether the function is a pure function (as in doesn't depend on, nor produce side effects) or not.
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <seastar/core/sstring.hh>
|
||||
#include "core/sstring.hh"
|
||||
#include "seastarx.hh"
|
||||
#include <iosfwd>
|
||||
#include <functional>
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
#include "cql3/sets.hh"
|
||||
#include "cql3/lists.hh"
|
||||
#include "cql3/constants.hh"
|
||||
#include "database.hh"
|
||||
#include "types/map.hh"
|
||||
#include "types/set.hh"
|
||||
#include "types/list.hh"
|
||||
|
||||
namespace cql3 {
|
||||
namespace functions {
|
||||
@@ -63,17 +59,17 @@ functions::init() {
|
||||
for (auto&& type : cql3_type::values()) {
|
||||
// Note: because text and varchar ends up being synonymous, our automatic makeToBlobFunction doesn't work
|
||||
// for varchar, so we special case it below. We also skip blob for obvious reasons.
|
||||
if (type == cql3_type::blob) {
|
||||
if (type == cql3_type::varchar || type == cql3_type::blob) {
|
||||
continue;
|
||||
}
|
||||
// counters are not supported yet
|
||||
if (type.is_counter()) {
|
||||
if (type->is_counter()) {
|
||||
warn(unimplemented::cause::COUNTERS);
|
||||
continue;
|
||||
}
|
||||
|
||||
declare(make_to_blob_function(type.get_type()));
|
||||
declare(make_from_blob_function(type.get_type()));
|
||||
declare(make_to_blob_function(type->get_type()));
|
||||
declare(make_from_blob_function(type->get_type()));
|
||||
}
|
||||
declare(aggregate_fcts::make_count_function<int8_t>());
|
||||
declare(aggregate_fcts::make_max_function<int8_t>());
|
||||
@@ -127,9 +123,10 @@ functions::init() {
|
||||
declare(aggregate_fcts::make_max_function<utils::UUID>());
|
||||
declare(aggregate_fcts::make_min_function<utils::UUID>());
|
||||
|
||||
declare(aggregate_fcts::make_count_function<bytes>());
|
||||
declare(aggregate_fcts::make_max_function<bytes>());
|
||||
declare(aggregate_fcts::make_min_function<bytes>());
|
||||
//FIXME:
|
||||
//declare(aggregate_fcts::make_count_function<bytes>());
|
||||
//declare(aggregate_fcts::make_max_function<bytes>());
|
||||
//declare(aggregate_fcts::make_min_function<bytes>());
|
||||
|
||||
// FIXME: more count/min/max
|
||||
|
||||
@@ -166,7 +163,7 @@ functions::make_arg_spec(const sstring& receiver_ks, const sstring& receiver_cf,
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||
return ::make_shared<column_specification>(receiver_ks,
|
||||
receiver_cf,
|
||||
::make_shared<column_identifier>(format("arg{:d}({})", i, name), true),
|
||||
::make_shared<column_identifier>(sprint("arg%d(%s)", i, name), true),
|
||||
fun.arg_types()[i]);
|
||||
}
|
||||
|
||||
@@ -286,13 +283,13 @@ functions::get(database& db,
|
||||
|
||||
if (compatibles.empty()) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
format("Invalid call to function {}, none of its type signatures match (known type signatures: {})",
|
||||
sprint("Invalid call to function %s, none of its type signatures match (known type signatures: %s)",
|
||||
name, join(", ", candidates)));
|
||||
}
|
||||
|
||||
if (compatibles.size() > 1) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
format("Ambiguous call to function {} (can be matched by following signatures: {}): use type casts to disambiguate",
|
||||
sprint("Ambiguous call to function %s (can be matched by following signatures: %s): use type casts to disambiguate",
|
||||
name, join(", ", compatibles)));
|
||||
}
|
||||
|
||||
@@ -331,7 +328,7 @@ functions::validate_types(database& db,
|
||||
const sstring& receiver_cf) {
|
||||
if (provided_args.size() != fun->arg_types().size()) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
format("Invalid number of arguments in call to function {}: {:d} required but {:d} provided",
|
||||
sprint("Invalid number of arguments in call to function %s: %d required but %d provided",
|
||||
fun->name(), fun->arg_types().size(), provided_args.size()));
|
||||
}
|
||||
|
||||
@@ -347,7 +344,7 @@ functions::validate_types(database& db,
|
||||
auto&& expected = make_arg_spec(receiver_ks, receiver_cf, *fun, i);
|
||||
if (!is_assignable(provided->test_assignment(db, keyspace, expected))) {
|
||||
throw exceptions::invalid_request_exception(
|
||||
format("Type error: {} cannot be passed as argument {:d} of function {} of type {}",
|
||||
sprint("Type error: %s cannot be passed as argument %d of function %s of type %s",
|
||||
provided, i, fun->name(), expected->type->as_cql3_type()));
|
||||
}
|
||||
}
|
||||
@@ -422,7 +419,7 @@ function_call::bind_and_get(const query_options& options) {
|
||||
// simplify things.
|
||||
auto val = t->bind_and_get(options);
|
||||
if (!val) {
|
||||
throw exceptions::invalid_request_exception(format("Invalid null value for argument to {}", *_fun));
|
||||
throw exceptions::invalid_request_exception(sprint("Invalid null value for argument to %s", *_fun));
|
||||
}
|
||||
buffers.push_back(std::move(to_bytes_opt(val)));
|
||||
}
|
||||
@@ -436,13 +433,13 @@ function_call::execute_internal(cql_serialization_format sf, scalar_function& fu
|
||||
try {
|
||||
// Check the method didn't lied on it's declared return type
|
||||
if (result) {
|
||||
fun.return_type()->validate(*result, sf);
|
||||
fun.return_type()->validate(*result);
|
||||
}
|
||||
return result;
|
||||
} catch (marshal_exception& e) {
|
||||
throw runtime_exception(format("Return of function {} ({}) is not a valid value for its declared return type {}",
|
||||
throw runtime_exception(sprint("Return of function %s (%s) is not a valid value for its declared return type %s",
|
||||
fun, to_hex(result),
|
||||
fun.return_type()->as_cql3_type()
|
||||
*fun.return_type()->as_cql3_type()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -464,9 +461,9 @@ function_call::make_terminal(shared_ptr<function> fun, cql3::raw_value result, c
|
||||
}
|
||||
|
||||
auto ctype = static_pointer_cast<const collection_type_impl>(fun->return_type());
|
||||
fragmented_temporary_buffer::view res;
|
||||
bytes_view res;
|
||||
if (result) {
|
||||
res = fragmented_temporary_buffer::view(bytes_view(*result));
|
||||
res = *result;
|
||||
}
|
||||
if (&ctype->_kind == &collection_type_impl::kind::list) {
|
||||
return make_shared(lists::value::from_serialized(std::move(res), static_pointer_cast<const list_type_impl>(ctype), sf));
|
||||
@@ -488,7 +485,7 @@ function_call::raw::prepare(database& db, const sstring& keyspace, ::shared_ptr<
|
||||
});
|
||||
auto&& fun = functions::functions::get(db, keyspace, _name, args, receiver->ks_name, receiver->cf_name, receiver);
|
||||
if (!fun) {
|
||||
throw exceptions::invalid_request_exception(format("Unknown function {} called", _name));
|
||||
throw exceptions::invalid_request_exception(sprint("Unknown function %s called", _name));
|
||||
}
|
||||
if (fun->is_aggregate()) {
|
||||
throw exceptions::invalid_request_exception("Aggregation function are not supported in the where clause");
|
||||
@@ -500,13 +497,13 @@ function_call::raw::prepare(database& db, const sstring& keyspace, ::shared_ptr<
|
||||
// Functions.get() will complain if no function "name" type check with the provided arguments.
|
||||
// We still have to validate that the return type matches however
|
||||
if (!receiver->type->is_value_compatible_with(*scalar_fun->return_type())) {
|
||||
throw exceptions::invalid_request_exception(format("Type error: cannot assign result of function {} (type {}) to {} (type {})",
|
||||
throw exceptions::invalid_request_exception(sprint("Type error: cannot assign result of function %s (type %s) to %s (type %s)",
|
||||
fun->name(), fun->return_type()->as_cql3_type(),
|
||||
receiver->name, receiver->type->as_cql3_type()));
|
||||
}
|
||||
|
||||
if (scalar_fun->arg_types().size() != _terms.size()) {
|
||||
throw exceptions::invalid_request_exception(format("Incorrect number of arguments specified for function {} (expected {:d}, found {:d})",
|
||||
throw exceptions::invalid_request_exception(sprint("Incorrect number of arguments specified for function %s (expected %d, found %d)",
|
||||
fun->name(), fun->arg_types().size(), _terms.size()));
|
||||
}
|
||||
|
||||
@@ -565,7 +562,7 @@ function_call::raw::test_assignment(database& db, const sstring& keyspace, share
|
||||
|
||||
sstring
|
||||
function_call::raw::to_string() const {
|
||||
return format("{}({})", _name, join(", ", _terms));
|
||||
return sprint("%s(%s)", _name, join(", ", _terms));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -59,6 +59,13 @@ namespace cql3 {
|
||||
|
||||
namespace functions {
|
||||
|
||||
#if 0
|
||||
// We special case the token function because that's the only function whose argument types actually
|
||||
// depend on the table on which the function is called. Because it's the sole exception, it's easier
|
||||
// to handle it as a special case.
|
||||
private static final FunctionName TOKEN_FUNCTION_NAME = FunctionName.nativeFunction("token");
|
||||
#endif
|
||||
|
||||
class functions {
|
||||
static thread_local std::unordered_multimap<function_name, shared_ptr<function>> _declared;
|
||||
private:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user