From 16a4b2f3e0e4beb2f77a5c956a07e5ba40dfd29f Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Wed, 11 Feb 2015 15:18:10 +0100 Subject: [PATCH 01/52] Introduce enum_set Allows to take full advantage of compile-time information. Examples: enum class x { A, B, C }; using my_enum = super_enum; using my_enumset = enum_set; static_assert(my_enumset::frozen::contains(), "it should..."); assert(my_enumset::frozen::contains(x::A)); --- enum_set.hh | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 enum_set.hh diff --git a/enum_set.hh b/enum_set.hh new file mode 100644 index 0000000000..a20b2d7afd --- /dev/null +++ b/enum_set.hh @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2015 Cloudius Systems, Ltd. + */ + +#pragma once + +#include +#include +#include + +/** + * + * Allows to take full advantage of compile-time information when operating + * on a set of enum values. + * + * Examples: + * + * enum class x { A, B, C }; + * using my_enum = super_enum; + * using my_enumset = enum_set; + * + * static_assert(my_enumset::frozen::contains(), "it should..."); + * + * assert(my_enumset::frozen::contains(my_enumset::prepare())); + * + * assert(my_enumset::frozen::contains(x::A)); + * + */ + + +template +struct super_enum { + using enum_type = EnumType; + + template + struct max { + static constexpr enum_type max_of(enum_type a, enum_type b) { + return a > b ? a : b; + } + + template + static constexpr enum_type get() { + return max_of(first, get()); + } + + template + static constexpr enum_type get() { return first; } + + static constexpr enum_type value = get(); + }; + + template + struct min { + static constexpr enum_type min_of(enum_type a, enum_type b) { + return a < b ? a : b; + } + + template + static constexpr enum_type get() { + return min_of(first, get()); + } + + template + static constexpr enum_type get() { return first; } + + static constexpr enum_type value = get(); + }; + + using sequence_type = typename std::underlying_type::type; + + template + static constexpr sequence_type sequence_for() { + return static_cast(Elem); + } + + static sequence_type sequence_for(enum_type elem) { + return static_cast(elem); + } + + static constexpr sequence_type max_sequence = sequence_for::value>(); + static constexpr sequence_type min_sequence = sequence_for::value>(); + + static_assert(min_sequence >= 0, "negative enum values unsupported"); +}; + +template +struct enum_set { + using mask_type = size_t; // TODO: use the smallest sufficient type + using enum_type = typename Enum::enum_type; + + static inline mask_type mask_for(enum_type e) { + return mask_type(1) << Enum::sequence_for(e); + } + + template + static constexpr mask_type mask_for() { + // FIXME: for some reason Enum::sequence_for() does not compile + return mask_type(1) << static_cast(Elem); + } + + struct prepared { + mask_type mask; + bool operator==(const prepared& o) const { + return mask == o.mask; + } + }; + + static prepared prepare(enum_type e) { + return {mask_for(e)}; + } + + template + static constexpr prepared prepare() { + return {mask_for()}; + } + + static_assert(std::numeric_limits::max() >= ((size_t)1 << Enum::max_sequence), "mask type too small"); + + template + struct frozen { + template + static constexpr mask_type make_mask() { + return mask_for(); + } + + static constexpr mask_type make_mask() { + return 0; + } + + template + static constexpr mask_type make_mask() { + return mask_for() | make_mask(); + } + + static constexpr mask_type mask = make_mask(); + + template + static constexpr bool contains() { + return mask & mask_for(); + } + + static bool contains(enum_type e) { + return mask & mask_for(e); + } + + static bool contains(prepared e) { + return mask & e.mask; + } + }; +}; From 8d03ebb8a8975cf74955060dad385904d47dda4b Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:51:55 +0100 Subject: [PATCH 02/52] to_string: Add missing includes --- to_string.hh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/to_string.hh b/to_string.hh index 65bcba6556..f7ba0fbe85 100644 --- a/to_string.hh +++ b/to_string.hh @@ -4,6 +4,10 @@ #pragma once +#include "core/sstring.hh" +#include +#include + /** * Converts a vector of pointers to Printable elements. * Printable is an object which has to_string() method. From 06ccaa3b5bf7ae620920b954c06dbdaf9b9c9ef3 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 09:28:05 +0100 Subject: [PATCH 03/52] db: Move method definitions to source file --- database.cc | 51 +++++++++++++++++++++++++++++++++- database.hh | 80 ++++++----------------------------------------------- 2 files changed, 59 insertions(+), 72 deletions(-) diff --git a/database.cc b/database.cc index e26c127e67..f9a890ab46 100644 --- a/database.cc +++ b/database.cc @@ -297,7 +297,8 @@ compare_for_merge(const column_definition& def, } } -void mutation_partition::apply(schema_ptr schema, mutation_partition&& p) { +void +mutation_partition::apply(schema_ptr schema, mutation_partition&& p) { _tombstone.apply(p._tombstone); for (auto&& entry : p._row_tombstones) { @@ -333,3 +334,51 @@ void mutation_partition::apply(schema_ptr schema, mutation_partition&& p) { } } } + +tombstone +mutation_partition::tombstone_for_row(schema_ptr schema, const clustering_key& key) { + tombstone t = _tombstone; + + auto i = _row_tombstones.lower_bound(key); + if (i != _row_tombstones.end() && schema->clustering_key_prefix_type->is_prefix_of(i->first, key)) { + t.apply(i->second); + } + + auto j = _rows.find(key); + if (j != _rows.end()) { + t.apply(j->second.t); + } + + return t; +} + +void +mutation_partition::apply_row_tombstone(schema_ptr schema, std::pair row_tombstone) { + auto& prefix = row_tombstone.first; + auto i = _row_tombstones.lower_bound(prefix); + if (i == _row_tombstones.end() || !schema->clustering_key_prefix_type->equal(prefix, i->first)) { + _row_tombstones.emplace_hint(i, std::move(row_tombstone)); + } else if (row_tombstone.second > i->second) { + i->second = row_tombstone.second; + } +} + +void +mutation_partition::apply_delete(schema_ptr schema, const clustering_prefix& prefix, tombstone t) { + if (prefix.empty()) { + apply(t); + } else if (prefix.size() == schema->clustering_key.size()) { + _rows[serialize_value(*schema->clustering_key_type, prefix)].t.apply(t); + } else { + apply_row_tombstone(schema, {serialize_value(*schema->clustering_key_prefix_type, prefix), t}); + } +} + +row* +mutation_partition::find_row(const clustering_key& key) { + auto i = _rows.find(key); + if (i == _rows.end()) { + return nullptr; + } + return &i->second.cells; +} diff --git a/database.hh b/database.hh index 197c52a19d..7fbb0430b3 100644 --- a/database.hh +++ b/database.hh @@ -157,81 +157,19 @@ public: : _rows(key_compare(s->clustering_key_type)) , _row_tombstones(serialized_compare(s->clustering_key_prefix_type)) { } - - void apply(tombstone t) { - _tombstone.apply(t); - } - - void apply_delete(schema_ptr schema, const clustering_prefix& prefix, tombstone t) { - if (prefix.empty()) { - apply(t); - } else if (prefix.size() == schema->clustering_key.size()) { - _rows[serialize_value(*schema->clustering_key_type, prefix)].t.apply(t); - } else { - apply_row_tombstone(schema, {serialize_value(*schema->clustering_key_prefix_type, prefix), t}); - } - } - + void apply(tombstone t) { _tombstone.apply(t); } + void apply_delete(schema_ptr schema, const clustering_prefix& prefix, tombstone t); void apply_row_tombstone(schema_ptr schema, bytes prefix, tombstone t) { apply_row_tombstone(schema, {std::move(prefix), std::move(t)}); } - - void apply_row_tombstone(schema_ptr schema, std::pair row_tombstone) { - auto& prefix = row_tombstone.first; - auto i = _row_tombstones.lower_bound(prefix); - if (i == _row_tombstones.end() || !schema->clustering_key_prefix_type->equal(prefix, i->first)) { - _row_tombstones.emplace_hint(i, std::move(row_tombstone)); - } else if (row_tombstone.second > i->second) { - i->second = row_tombstone.second; - } - } - + void apply_row_tombstone(schema_ptr schema, std::pair row_tombstone); void apply(schema_ptr schema, mutation_partition&& p); - - const row_tombstone_set& row_tombstones() { - return _row_tombstones; - } - - row& static_row() { - return _static_row; - } - - row& clustered_row(const clustering_key& key) { - return _rows[key].cells; - } - - row& clustered_row(clustering_key&& key) { - return _rows[std::move(key)].cells; - } - - row* find_row(const clustering_key& key) { - auto i = _rows.find(key); - if (i == _rows.end()) { - return nullptr; - } - return &i->second.cells; - } - - /** - * Returns the base tombstone for all cells of given clustering row. Such tombstone - * holds all information necessary to decide whether cells in a row are deleted or not, - * in addition to any information inside individual cells. - */ - tombstone tombstone_for_row(schema_ptr schema, const clustering_key& key) { - tombstone t = _tombstone; - - auto i = _row_tombstones.lower_bound(key); - if (i != _row_tombstones.end() && schema->clustering_key_prefix_type->is_prefix_of(i->first, key)) { - t.apply(i->second); - } - - auto j = _rows.find(key); - if (j != _rows.end()) { - t.apply(j->second.t); - } - - return t; - } + const row_tombstone_set& row_tombstones() const { return _row_tombstones; } + row& static_row() { return _static_row; } + row& clustered_row(const clustering_key& key) { return _rows[key].cells; } + row& clustered_row(clustering_key&& key) { return _rows[std::move(key)].cells; } + row* find_row(const clustering_key& key); + tombstone tombstone_for_row(schema_ptr schema, const clustering_key& key); }; class mutation final { From f450264a26e15715c2ef7c77fc4d161146191b87 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 20:17:48 +0100 Subject: [PATCH 04/52] cql3: Remove duplicated definition of bytes_opt --- cql3/term.hh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cql3/term.hh b/cql3/term.hh index 93c919be38..b925cb521a 100644 --- a/cql3/term.hh +++ b/cql3/term.hh @@ -29,6 +29,7 @@ #include "variable_specifications.hh" #include "cql3/assignment_testable.hh" #include "cql3/query_options.hh" +#include "types.hh" namespace cql3 { @@ -36,8 +37,6 @@ class terminal; class term; -using bytes_opt = std::experimental::optional; - /** * A CQL3 term, i.e. a column value with or without bind variables. * From 3a2bf359b83e9d3a6d1086d44d5b9ed83d4b46f4 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 20:18:05 +0100 Subject: [PATCH 05/52] cql3: Add to_string() virtual method to terminal class --- cql3/term.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cql3/term.hh b/cql3/term.hh index b925cb521a..6be55d3fe2 100644 --- a/cql3/term.hh +++ b/cql3/term.hh @@ -159,6 +159,8 @@ public: virtual bytes_opt bind_and_get(const query_options& options) override { return get(options); } + + virtual sstring to_string() const = 0; }; class multi_item_terminal : public terminal { From 7ae8406ab2180ae1a1aeb448b592e11c47eba362 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 18:53:53 +0100 Subject: [PATCH 06/52] cql3: Convert constants.NULL_LITERAL --- configure.py | 1 + cql3/constants.cc | 8 ++++ cql3/constants.hh | 97 +++++++++++++++++------------------------------ 3 files changed, 43 insertions(+), 63 deletions(-) create mode 100644 cql3/constants.cc diff --git a/configure.py b/configure.py index 4de8b170a1..8da75fb6af 100755 --- a/configure.py +++ b/configure.py @@ -248,6 +248,7 @@ urchin_core = (['database.cc', 'cql3/operator.cc', 'cql3/relation.cc', 'cql3/column_identifier.cc', + 'cql3/constants.cc', 'db/db.cc', 'io/io.cc', 'utils/utils.cc', diff --git a/cql3/constants.cc b/cql3/constants.cc new file mode 100644 index 0000000000..9026eaca34 --- /dev/null +++ b/cql3/constants.cc @@ -0,0 +1,8 @@ +#include "cql3/constants.hh" + +namespace cql3 { + +const ::shared_ptr constants::NULL_LITERAL = ::make_shared(); +const ::shared_ptr constants::null_literal::NULL_VALUE = ::make_shared(); + +} \ No newline at end of file diff --git a/cql3/constants.hh b/cql3/constants.hh index d73dac31d5..29a4139dcb 100644 --- a/cql3/constants.hh +++ b/cql3/constants.hh @@ -28,6 +28,8 @@ #include "cql3/abstract_marker.hh" #include "cql3/update_parameters.hh" #include "cql3/operation.hh" +#include "cql3/term.hh" +#include "core/shared_ptr.hh" namespace cql3 { @@ -66,47 +68,48 @@ public: STRING, INTEGER, UUID, FLOAT, BOOLEAN, HEX }; -#if 0 - public static final Term.Raw NULL_LITERAL = new Term.Raw() - { - private final Term.Terminal NULL_VALUE = new Value(null) - { - @Override - public Terminal bind(QueryOptions options) - { - // We return null because that makes life easier for collections - return null; - } + /** + * A constant value, i.e. a ByteBuffer. + */ + class value : public terminal { + public: + bytes_opt _bytes; + value(bytes_opt bytes_) : _bytes(std::move(bytes_)) {} + virtual bytes_opt get(const query_options& options) override { return _bytes; } + virtual bytes_opt bind_and_get(const query_options& options) override { return _bytes; } + virtual sstring to_string() const override { return to_hex(*_bytes); } + }; - @Override - public String toString() - { - return "null"; - } + class null_literal final : public term::raw { + private: + class null_value final : public value { + public: + null_value() : value({}) {} + virtual ::shared_ptr bind(const query_options& options) override { return {}; } + virtual sstring to_string() const override { return "null"; } }; - - public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException - { - if (!testAssignment(keyspace, receiver).isAssignable()) - throw new InvalidRequestException("Invalid null value for counter increment/decrement"); - + static const ::shared_ptr NULL_VALUE; + public: + virtual ::shared_ptr prepare(const sstring& keyspace, ::shared_ptr receiver) override { + if (!is_assignable(test_assignment(keyspace, receiver))) { + throw exceptions::invalid_request_exception("Invalid null value for counter increment/decrement"); + } return NULL_VALUE; } - public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver) - { - return receiver.type instanceof CounterColumnType - ? AssignmentTestable.TestResult.NOT_ASSIGNABLE - : AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; + virtual assignment_testable::test_result test_assignment(const sstring& keyspace, + ::shared_ptr receiver) override { + return receiver->type->is_counter() + ? assignment_testable::test_result::NOT_ASSIGNABLE + : assignment_testable::test_result::WEAKLY_ASSIGNABLE; } - @Override - public String toString() - { + virtual sstring to_string() override { return "null"; } }; -#endif + + static const ::shared_ptr NULL_LITERAL; class literal : public term::raw { private: @@ -257,38 +260,6 @@ public: } }; -#if 0 - /** - * A constant value, i.e. a ByteBuffer. - */ - public static class Value extends Term.Terminal - { - public final ByteBuffer bytes; - - public Value(ByteBuffer bytes) - { - this.bytes = bytes; - } - - public ByteBuffer get(QueryOptions options) - { - return bytes; - } - - @Override - public ByteBuffer bindAndGet(QueryOptions options) - { - return bytes; - } - - @Override - public String toString() - { - return ByteBufferUtil.bytesToHex(bytes); - } - } -#endif - class marker : public abstract_marker { public: marker(int32_t bind_index, ::shared_ptr receiver) From 2f9476d7126329378e90402c2d90aebcb8ed42d3 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 20:20:52 +0100 Subject: [PATCH 07/52] cql3: Move parameters into fields rather than copy --- cql3/statements/update_statement.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cql3/statements/update_statement.hh b/cql3/statements/update_statement.hh index d2cad8c09d..9dbc1f4969 100644 --- a/cql3/statements/update_statement.hh +++ b/cql3/statements/update_statement.hh @@ -94,9 +94,9 @@ private: std::vector<::shared_ptr> column_names, std::vector<::shared_ptr> column_values, bool if_not_exists) - : modification_statement::parsed{std::move(name), attrs, conditions_vector{}, if_not_exists, false} - , _column_names{column_names} - , _column_values{column_values} + : modification_statement::parsed{std::move(name), std::move(attrs), conditions_vector{}, if_not_exists, false} + , _column_names{std::move(column_names)} + , _column_values{std::move(column_values)} { } virtual ::shared_ptr prepare_internal(schema_ptr schema, From cecf06f44d784cb9428adfed86c9ec0d7c4d8f2a Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 20:14:48 +0100 Subject: [PATCH 08/52] cql3: Move parser out of cql3 namespace This moves parser from cql3 to cql3_parser namespace to avoid name conflicts between parser rules and classes defined in the cql3 namespace. In particular the "term" rule would conflict with the "term" class, leading to compilation errors. I figured it will be easier to change namespace than to rename all rule occurances. --- cql3/Cql.g | 53 +++++++++++++++++++++++---------------------- transport/server.cc | 8 +++---- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/cql3/Cql.g b/cql3/Cql.g index 256a61cbff..d8751960fc 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -23,7 +23,7 @@ options { language = Cpp; } -@parser::namespace{cql3} +@parser::namespace{cql3_parser} @parser::includes { #include "cql3/statements/select_statement.hh" @@ -39,9 +39,10 @@ options { } @parser::traits { -using namespace cql3; using namespace cql3::statements; using namespace cql3::selection; +using cql3::cql3_type; +using cql3::native_cql3_type; } @header { @@ -191,7 +192,7 @@ using namespace cql3::selection; #endif } -@lexer::namespace{cql3} +@lexer::namespace{cql3_parser} @lexer::traits { class CqlLexer; @@ -346,7 +347,7 @@ selectClause returns [std::vector> expr] ; selector returns [shared_ptr s] - @init{ shared_ptr alias; } + @init{ shared_ptr alias; } : us=unaliasedSelector (K_AS c=ident { alias = c; })? { $s = make_shared(us, alias); } ; @@ -981,22 +982,22 @@ userOption[UserOptions opts] // Column Identifiers. These need to be treated differently from other // identifiers because the underlying comparator is not necessarily text. See // CASSANDRA-8178 for details. -cident returns [shared_ptr id] - : t=IDENT { $id = make_shared(sstring{$t.text}, false); } - | t=QUOTED_NAME { $id = make_shared(sstring{$t.text}, true); } - | k=unreserved_keyword { $id = make_shared(k, false); } +cident returns [shared_ptr id] + : t=IDENT { $id = make_shared(sstring{$t.text}, false); } + | t=QUOTED_NAME { $id = make_shared(sstring{$t.text}, true); } + | k=unreserved_keyword { $id = make_shared(k, false); } ; // Identifiers that do not refer to columns or where the comparator is known to be text -ident returns [shared_ptr id] - : t=IDENT { $id = make_shared(sstring{$t.text}, false); } - | t=QUOTED_NAME { $id = make_shared(sstring{$t.text}, true); } - | k=unreserved_keyword { $id = make_shared(k, false); } +ident returns [shared_ptr id] + : t=IDENT { $id = make_shared(sstring{$t.text}, false); } + | t=QUOTED_NAME { $id = make_shared(sstring{$t.text}, true); } + | k=unreserved_keyword { $id = make_shared(k, false); } ; // Keyspace & Column family names keyspaceName returns [sstring id] - @init { auto name = make_shared(); } + @init { auto name = make_shared(); } : cfOrKsName[name, true] { $id = name->get_keyspace(); } ; @@ -1013,8 +1014,8 @@ idxOrKsName[IndexName name, boolean isKs] ; #endif -columnFamilyName returns [shared_ptr name] - @init { $name = make_shared(); } +columnFamilyName returns [shared_ptr name] + @init { $name = make_shared(); } : (cfOrKsName[name, true] '.')? cfOrKsName[name, false] ; @@ -1024,7 +1025,7 @@ userTypeName returns [UTName name] ; #endif -cfOrKsName[shared_ptr name, bool isKs] +cfOrKsName[shared_ptr name, bool isKs] : t=IDENT { if (isKs) $name->set_keyspace($t.text, false); else $name->set_column_family($t.text, false); } | t=QUOTED_NAME { if (isKs) $name->set_keyspace($t.text, true); else $name->set_column_family($t.text, true); } | k=unreserved_keyword { if (isKs) $name->set_keyspace(k, false); else $name->set_column_family(k, false); } @@ -1033,15 +1034,15 @@ cfOrKsName[shared_ptr name, bool isKs] #endif ; -constant returns [shared_ptr constant] +constant returns [shared_ptr constant] @init{std::string sign;} - : t=STRING_LITERAL { $constant = constants::literal::string(sstring{$t.text}); } - | t=INTEGER { $constant = constants::literal::integer(sstring{$t.text}); } - | t=FLOAT { $constant = constants::literal::floating_point(sstring{$t.text}); } - | t=BOOLEAN { $constant = constants::literal::bool_(sstring{$t.text}); } - | t=UUID { $constant = constants::literal::uuid(sstring{$t.text}); } - | t=HEXNUMBER { $constant = constants::literal::hex(sstring{$t.text}); } - | { sign=""; } ('-' {sign = "-"; } )? t=(K_NAN | K_INFINITY) { $constant = constants::literal::floating_point(sstring{sign + $t.text}); } + : t=STRING_LITERAL { $constant = cql3::constants::literal::string(sstring{$t.text}); } + | t=INTEGER { $constant = cql3::constants::literal::integer(sstring{$t.text}); } + | t=FLOAT { $constant = cql3::constants::literal::floating_point(sstring{$t.text}); } + | t=BOOLEAN { $constant = cql3::constants::literal::bool_(sstring{$t.text}); } + | t=UUID { $constant = cql3::constants::literal::uuid(sstring{$t.text}); } + | t=HEXNUMBER { $constant = cql3::constants::literal::hex(sstring{$t.text}); } + | { sign=""; } ('-' {sign = "-"; } )? t=(K_NAN | K_INFINITY) { $constant = cql3::constants::literal::floating_point(sstring{sign + $t.text}); } ; #if 0 @@ -1094,9 +1095,9 @@ value returns [Term.Raw value] ; #endif -intValue returns [::shared_ptr value] +intValue returns [::shared_ptr value] : - | t=INTEGER { $value = constants::literal::integer(sstring{$t.text}); } + | t=INTEGER { $value = cql3::constants::literal::integer(sstring{$t.text}); } #if 0 | ':' id=ident { $value = newBindVariables(id); } | QMARK { $value = newBindVariables(null); } diff --git a/transport/server.cc b/transport/server.cc index 012f984f03..bcd71fde4d 100644 --- a/transport/server.cc +++ b/transport/server.cc @@ -362,10 +362,10 @@ future<> cql_server::connection::process_query(uint16_t stream, temporary_buffer auto flags = read_byte(buf); #endif print("processing query: '%s' ...\n", query); - cql3::CqlLexer::InputStreamType input{reinterpret_cast(query.begin()), ANTLR_ENC_UTF8, static_cast(query.size()), nullptr}; - cql3::CqlLexer lexer{&input}; - cql3::CqlParser::TokenStreamType tstream(ANTLR_SIZE_HINT, lexer.get_tokSource()); - cql3::CqlParser parser{&tstream}; + cql3_parser::CqlLexer::InputStreamType input{reinterpret_cast(query.begin()), ANTLR_ENC_UTF8, static_cast(query.size()), nullptr}; + cql3_parser::CqlLexer lexer{&input}; + cql3_parser::CqlParser::TokenStreamType tstream(ANTLR_SIZE_HINT, lexer.get_tokSource()); + cql3_parser::CqlParser parser{&tstream}; auto stmt = parser.query(); assert(stmt != nullptr); return make_ready_future<>(); From a7cf24b010e75d066a2799f7c0a3e9e6cb4c4eba Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 09:11:25 +0100 Subject: [PATCH 09/52] cql3: Take cf_name as shared_ptr<> It's allocated as one in the parser so it's easier to just pass it along. Later we may decide to change it to unique_ptr or optional<>. --- cql3/statements/cf_statement.hh | 4 ++-- cql3/statements/modification_statement.hh | 2 +- cql3/statements/schema_altering_statement.hh | 4 ++-- cql3/statements/truncate_statement.hh | 2 +- cql3/statements/update_statement.hh | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cql3/statements/cf_statement.hh b/cql3/statements/cf_statement.hh index 20321fc891..f1c0969199 100644 --- a/cql3/statements/cf_statement.hh +++ b/cql3/statements/cf_statement.hh @@ -39,9 +39,9 @@ namespace statements { */ class cf_statement : public parsed_statement { protected: - std::experimental::optional _cf_name; + ::shared_ptr _cf_name; - cf_statement(std::experimental::optional&& cf_name) + cf_statement(::shared_ptr&& cf_name) : _cf_name(std::move(cf_name)) { } diff --git a/cql3/statements/modification_statement.hh b/cql3/statements/modification_statement.hh index dc01ea8be9..d760bfe119 100644 --- a/cql3/statements/modification_statement.hh +++ b/cql3/statements/modification_statement.hh @@ -466,7 +466,7 @@ public: const bool _if_not_exists; const bool _if_exists; protected: - parsed(std::experimental::optional name, ::shared_ptr attrs, const conditions_vector& conditions, bool if_not_exists, bool if_exists) + parsed(::shared_ptr name, ::shared_ptr attrs, const conditions_vector& conditions, bool if_not_exists, bool if_exists) : cf_statement{std::move(name)} , _attrs{attrs} , _conditions{conditions} diff --git a/cql3/statements/schema_altering_statement.hh b/cql3/statements/schema_altering_statement.hh index 6e5bcbdb5a..3ba3f8a59e 100644 --- a/cql3/statements/schema_altering_statement.hh +++ b/cql3/statements/schema_altering_statement.hh @@ -45,11 +45,11 @@ private: protected: schema_altering_statement() - : cf_statement{std::experimental::optional{}} + : cf_statement{::shared_ptr{}} , _is_column_family_level{false} { } - schema_altering_statement(std::experimental::optional&& name) + schema_altering_statement(::shared_ptr&& name) : cf_statement{std::move(name)} , _is_column_family_level{true} { } diff --git a/cql3/statements/truncate_statement.hh b/cql3/statements/truncate_statement.hh index f376e281b1..11c0440212 100644 --- a/cql3/statements/truncate_statement.hh +++ b/cql3/statements/truncate_statement.hh @@ -36,7 +36,7 @@ namespace statements { class truncate_statement : public cf_statement, public virtual cql_statement, public ::enable_shared_from_this { public: - truncate_statement(std::experimental::optional&& name) + truncate_statement(::shared_ptr&& name) : cf_statement{std::move(name)} { } diff --git a/cql3/statements/update_statement.hh b/cql3/statements/update_statement.hh index 9dbc1f4969..e55f38bc5c 100644 --- a/cql3/statements/update_statement.hh +++ b/cql3/statements/update_statement.hh @@ -89,7 +89,7 @@ private: * @param columnValues list of column values (corresponds to names) * @param attrs additional attributes for statement (CL, timestamp, timeToLive) */ - parsed_insert(std::experimental::optional name, + parsed_insert(::shared_ptr name, ::shared_ptr attrs, std::vector<::shared_ptr> column_names, std::vector<::shared_ptr> column_values, @@ -119,7 +119,7 @@ private: * @param updates a map of column operations to perform * @param whereClause the where clause */ - parsed_update(std::experimental::optional name, + parsed_update(::shared_ptr name, ::shared_ptr attrs, std::vector, ::shared_ptr>> updates, std::vector where_clause, From 524c6a4e402bf1936836c3d82b3e61e44ab1b4eb Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Wed, 11 Feb 2015 15:25:54 +0100 Subject: [PATCH 10/52] cql3: Implement modification_statement::validate() --- cql3/statements/modification_statement.cc | 15 +++++++++++++++ cql3/statements/modification_statement.hh | 14 +------------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/cql3/statements/modification_statement.cc b/cql3/statements/modification_statement.cc index da880e7ae7..b9eadbf8b8 100644 --- a/cql3/statements/modification_statement.cc +++ b/cql3/statements/modification_statement.cc @@ -446,6 +446,21 @@ modification_statement::parsed::prepare(database& db, ::shared_ptris_timestamp_set()) { + throw exceptions::invalid_request_exception("Cannot provide custom timestamp for conditional updates"); + } + + if (is_counter() && attrs->is_timestamp_set()) { + throw exceptions::invalid_request_exception("Cannot provide custom timestamp for counter updates"); + } + + if (is_counter() && attrs->is_time_to_live_set()) { + throw exceptions::invalid_request_exception("Cannot provide custom TTL for counter updates"); + } +} + } } diff --git a/cql3/statements/modification_statement.hh b/cql3/statements/modification_statement.hh index d760bfe119..2e9d29c59f 100644 --- a/cql3/statements/modification_statement.hh +++ b/cql3/statements/modification_statement.hh @@ -201,19 +201,7 @@ public: throw std::runtime_error("not implemented"); } - virtual void validate(const service::client_state& state) override { -#if 0 - if (hasConditions() && attrs.isTimestampSet()) - throw new InvalidRequestException("Cannot provide custom timestamp for conditional updates"); - - if (isCounter() && attrs.isTimestampSet()) - throw new InvalidRequestException("Cannot provide custom timestamp for counter updates"); - - if (isCounter() && attrs.isTimeToLiveSet()) - throw new InvalidRequestException("Cannot provide custom TTL for counter updates"); -#endif - throw std::runtime_error("not implemented"); - } + virtual void validate(const service::client_state& state); void add_operation(::shared_ptr op) { if (op->column.is_static()) { From 3d7122dc90c1b3b841bd76b32a4f2df6fcfc22fc Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Wed, 11 Feb 2015 15:26:41 +0100 Subject: [PATCH 11/52] cql3: Do not fail because of unimplemented check_access() Turn it into a warning. --- cql3/statements/modification_statement.hh | 2 +- unimplemented.hh | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cql3/statements/modification_statement.hh b/cql3/statements/modification_statement.hh index 2e9d29c59f..a862e1070b 100644 --- a/cql3/statements/modification_statement.hh +++ b/cql3/statements/modification_statement.hh @@ -191,6 +191,7 @@ public: } virtual void check_access(const service::client_state& state) override { + unimplemented::permissions(); #if 0 state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.MODIFY); @@ -198,7 +199,6 @@ public: if (hasConditions()) state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.SELECT); #endif - throw std::runtime_error("not implemented"); } virtual void validate(const service::client_state& state); diff --git a/unimplemented.hh b/unimplemented.hh index 04a80af642..29fe75ebd6 100644 --- a/unimplemented.hh +++ b/unimplemented.hh @@ -40,6 +40,11 @@ void auth() { warn("auth"); } +static inline +void permissions() { + warn("permissions"); +} + static inline void triggers() { warn("triggers"); From e3054daacf4b61ce921a059c62a5c0d0e12be61f Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 20:22:33 +0100 Subject: [PATCH 12/52] cql3: Convert parser rule for INSERT statement --- cql3/Cql.g | 56 +++++++++++++++++------------ cql3/statements/update_statement.hh | 2 +- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/cql3/Cql.g b/cql3/Cql.g index d8751960fc..ea3d5af71b 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -27,6 +27,7 @@ options { @parser::includes { #include "cql3/statements/select_statement.hh" +#include "cql3/statements/update_statement.hh" #include "cql3/statements/use_statement.hh" #include "cql3/selection/raw_selector.hh" #include "cql3/constants.hh" @@ -260,8 +261,8 @@ cqlStatement returns [shared_ptr stmt] @after{ if (stmt != null) stmt.setBoundVariables(bindVariables); } #endif : st1= selectStatement { $stmt = st1; } -#if 0 | st2= insertStatement { $stmt = st2; } +#if 0 | st3= updateStatement { $stmt = st3; } | st4= batchStatement { $stmt = st4; } | st5= deleteStatement { $stmt = st5; } @@ -394,6 +395,7 @@ orderByClause[Map orderings] } : c=cident (K_ASC | K_DESC { reversed = true; })? { orderings.put(c, reversed); } ; +#endif /** * INSERT INTO (, , , ...) @@ -401,38 +403,40 @@ orderByClause[Map orderings] * USING TIMESTAMP ; * */ -insertStatement returns [UpdateStatement.ParsedInsert expr] +insertStatement returns [::shared_ptr expr] @init { - Attributes.Raw attrs = new Attributes.Raw(); - List columnNames = new ArrayList(); - List values = new ArrayList(); - boolean ifNotExists = false; + auto attrs = ::make_shared(); + std::vector<::shared_ptr> column_names; + std::vector<::shared_ptr> values; + bool if_not_exists = false; } : K_INSERT K_INTO cf=columnFamilyName - '(' c1=cident { columnNames.add(c1); } ( ',' cn=cident { columnNames.add(cn); } )* ')' + '(' c1=cident { column_names.push_back(c1); } ( ',' cn=cident { column_names.push_back(cn); } )* ')' K_VALUES - '(' v1=term { values.add(v1); } ( ',' vn=term { values.add(vn); } )* ')' + '(' v1=term { values.push_back(v1); } ( ',' vn=term { values.push_back(vn); } )* ')' - ( K_IF K_NOT K_EXISTS { ifNotExists = true; } )? + ( K_IF K_NOT K_EXISTS { if_not_exists = true; } )? ( usingClause[attrs] )? { - $expr = new UpdateStatement.ParsedInsert(cf, - attrs, - columnNames, - values, - ifNotExists); + $expr = ::make_shared(std::move(cf), + std::move(attrs), + std::move(column_names), + std::move(values), + if_not_exists); } ; -usingClause[Attributes.Raw attrs] +usingClause[::shared_ptr attrs] : K_USING usingClauseObjective[attrs] ( K_AND usingClauseObjective[attrs] )* ; -usingClauseObjective[Attributes.Raw attrs] - : K_TIMESTAMP ts=intValue { attrs.timestamp = ts; } - | K_TTL t=intValue { attrs.timeToLive = t; } +usingClauseObjective[::shared_ptr attrs] + : K_TIMESTAMP ts=intValue { attrs->timestamp = ts; } + | K_TTL t=intValue { attrs->time_to_live = t; } ; +#if 0 + /** * UPDATE * USING TIMESTAMP @@ -1083,17 +1087,21 @@ tupleLiteral returns [Tuples.Literal tt] @after{ $tt = new Tuples.Literal(l); } : '(' t1=term { l.add(t1); } ( ',' tn=term { l.add(tn); } )* ')' ; +#endif -value returns [Term.Raw value] +value returns [::shared_ptr value] : c=constant { $value = c; } +#if 0 | l=collectionLiteral { $value = l; } | u=usertypeLiteral { $value = u; } | t=tupleLiteral { $value = t; } - | K_NULL { $value = Constants.NULL_LITERAL; } +#endif + | K_NULL { $value = cql3::constants::NULL_LITERAL; } +#if 0 | ':' id=ident { $value = newBindVariables(id); } | QMARK { $value = newBindVariables(null); } - ; #endif + ; intValue returns [::shared_ptr value] : @@ -1123,13 +1131,17 @@ functionArgs returns [List a] ( ',' tn=term { args.add(tn); } )* ')' { $a = args; } ; +#endif -term returns [Term.Raw term] +term returns [::shared_ptr term] : v=value { $term = v; } +#if 0 | f=functionName args=functionArgs { $term = new FunctionCall.Raw(f, args); } | '(' c=comparatorType ')' t=term { $term = new TypeCast(c, t); } +#endif ; +#if 0 columnOperation[List> operations] : key=cident columnOperationDifferentiator[operations, key] ; diff --git a/cql3/statements/update_statement.hh b/cql3/statements/update_statement.hh index e55f38bc5c..af11f8134b 100644 --- a/cql3/statements/update_statement.hh +++ b/cql3/statements/update_statement.hh @@ -75,7 +75,7 @@ private: } virtual void add_update_for_key(mutation& m, const clustering_prefix& prefix, const update_parameters& params) override; - +public: class parsed_insert : public modification_statement::parsed { private: const std::vector<::shared_ptr> _column_names; From f3130d395fb5b30c580d29ceb6a3d1ad600b8d72 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 21:13:04 +0100 Subject: [PATCH 13/52] cql3: Return shared_ptr instead of optional It's polymorphic type in Origin. --- cql3/cql_statement.hh | 4 ++-- cql3/statements/modification_statement.cc | 8 ++++---- cql3/statements/modification_statement.hh | 6 +++--- cql3/statements/schema_altering_statement.hh | 4 ++-- cql3/statements/truncate_statement.hh | 4 ++-- cql3/statements/use_statement.hh | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cql3/cql_statement.hh b/cql3/cql_statement.hh index 1c6de1a138..56678cf532 100644 --- a/cql3/cql_statement.hh +++ b/cql3/cql_statement.hh @@ -62,7 +62,7 @@ public: * @param state the current query state * @param options options for this query (consistency, variables, pageSize, ...) */ - virtual future> + virtual future<::shared_ptr> execute(service::storage_proxy& proxy, service::query_state& state, const query_options& options) = 0; /** @@ -70,7 +70,7 @@ public: * * @param state the current query state */ - virtual future> + virtual future<::shared_ptr> execute_internal(database& db, service::query_state& state, const query_options& options) = 0; virtual bool uses_function(const sstring& ks_name, const sstring& function_name) const = 0; diff --git a/cql3/statements/modification_statement.cc b/cql3/statements/modification_statement.cc index b9eadbf8b8..d920c0f38d 100644 --- a/cql3/statements/modification_statement.cc +++ b/cql3/statements/modification_statement.cc @@ -277,7 +277,7 @@ modification_statement::build_partition_keys(const query_options& options) { return result; } -future> +future<::shared_ptr> modification_statement::execute(service::storage_proxy& proxy, service::query_state& qs, const query_options& options) { if (has_conditions() && options.get_protocol_version() == 1) { throw new exceptions::invalid_request_exception("Conditional updates are not supported by the protocol version in use. You need to upgrade to a driver using the native protocol v2."); @@ -288,8 +288,8 @@ modification_statement::execute(service::storage_proxy& proxy, service::query_st } return execute_without_condition(proxy, qs, options).then([] { - return make_ready_future>( - std::experimental::optional{}); + return make_ready_future<::shared_ptr>( + ::shared_ptr{}); }); } @@ -310,7 +310,7 @@ modification_statement::execute_without_condition(service::storage_proxy& proxy, }); } -future> +future<::shared_ptr> modification_statement::execute_with_condition(service::storage_proxy& proxy, service::query_state& qs, const query_options& options) { unimplemented::lwt(); #if 0 diff --git a/cql3/statements/modification_statement.hh b/cql3/statements/modification_statement.hh index a862e1070b..13acdad485 100644 --- a/cql3/statements/modification_statement.hh +++ b/cql3/statements/modification_statement.hh @@ -283,10 +283,10 @@ public: return _if_not_exists || _if_exists || !_column_conditions.empty() || !_static_conditions.empty(); } - virtual future> + virtual future<::shared_ptr> execute(service::storage_proxy& proxy, service::query_state& qs, const query_options& options) override; - virtual future> + virtual future<::shared_ptr> execute_internal(database& db, service::query_state& qs, const query_options& options) override { throw std::runtime_error("not implemented"); } @@ -295,7 +295,7 @@ private: future<> execute_without_condition(service::storage_proxy& proxy, service::query_state& qs, const query_options& options); - future> + future<::shared_ptr> execute_with_condition(service::storage_proxy& proxy, service::query_state& qs, const query_options& options); #if 0 diff --git a/cql3/statements/schema_altering_statement.hh b/cql3/statements/schema_altering_statement.hh index 3ba3f8a59e..51ed67f5cd 100644 --- a/cql3/statements/schema_altering_statement.hh +++ b/cql3/statements/schema_altering_statement.hh @@ -81,7 +81,7 @@ protected: public abstract boolean announceMigration(boolean isLocalOnly) throws RequestValidationException; #endif - virtual future> + virtual future<::shared_ptr> execute(service::storage_proxy& proxy, service::query_state& state, const query_options& options) override { throw std::runtime_error("not implemented"); #if 0 @@ -96,7 +96,7 @@ protected: #endif } - virtual future> + virtual future<::shared_ptr> execute_internal(database& db, service::query_state& state, const query_options& options) override { throw std::runtime_error("unsupported operation"); #if 0 diff --git a/cql3/statements/truncate_statement.hh b/cql3/statements/truncate_statement.hh index 11c0440212..7e475ed695 100644 --- a/cql3/statements/truncate_statement.hh +++ b/cql3/statements/truncate_statement.hh @@ -62,7 +62,7 @@ public: #endif } - virtual future> + virtual future<::shared_ptr> execute(service::storage_proxy& proxy, service::query_state& state, const query_options& options) override { throw std::runtime_error("not implemented"); #if 0 @@ -86,7 +86,7 @@ public: #endif } - virtual future> + virtual future<::shared_ptr> execute_internal(database& db, service::query_state& state, const query_options& options) override { throw std::runtime_error("unsupported operation"); } diff --git a/cql3/statements/use_statement.hh b/cql3/statements/use_statement.hh index c641fabcdb..e48834bf07 100644 --- a/cql3/statements/use_statement.hh +++ b/cql3/statements/use_statement.hh @@ -60,7 +60,7 @@ public: virtual void validate(const service::client_state& state) override { } - virtual future> + virtual future<::shared_ptr> execute(service::storage_proxy& proxy, service::query_state& state, const query_options& options) override { throw std::runtime_error("not implemented"); #if 0 @@ -69,7 +69,7 @@ public: #endif } - virtual future> + virtual future<::shared_ptr> execute_internal(database& db, service::query_state& state, const query_options& options) override { // Internal queries are exclusively on the system keyspace and 'use' is thus useless throw std::runtime_error("unsupported operation"); From ae725626d5f792c0596bf701e2075b0a0269e137 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 21:58:04 +0100 Subject: [PATCH 14/52] exceptions: Import ExceptionCode.java --- exceptions/ExceptionCode.java | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 exceptions/ExceptionCode.java diff --git a/exceptions/ExceptionCode.java b/exceptions/ExceptionCode.java new file mode 100644 index 0000000000..ce082a79c0 --- /dev/null +++ b/exceptions/ExceptionCode.java @@ -0,0 +1,71 @@ +/* + * 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. + */ +package org.apache.cassandra.exceptions; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.cassandra.transport.ProtocolException; + +/** + * Exceptions code, as defined by the binary protocol. + */ +public enum ExceptionCode +{ + SERVER_ERROR (0x0000), + PROTOCOL_ERROR (0x000A), + + BAD_CREDENTIALS (0x0100), + + // 1xx: problem during request execution + UNAVAILABLE (0x1000), + OVERLOADED (0x1001), + IS_BOOTSTRAPPING(0x1002), + TRUNCATE_ERROR (0x1003), + WRITE_TIMEOUT (0x1100), + READ_TIMEOUT (0x1200), + + // 2xx: problem validating the request + SYNTAX_ERROR (0x2000), + UNAUTHORIZED (0x2100), + INVALID (0x2200), + CONFIG_ERROR (0x2300), + ALREADY_EXISTS (0x2400), + UNPREPARED (0x2500); + + public final int value; + private static final Map valueToCode = new HashMap(ExceptionCode.values().length); + static + { + for (ExceptionCode code : ExceptionCode.values()) + valueToCode.put(code.value, code); + } + + private ExceptionCode(int value) + { + this.value = value; + } + + public static ExceptionCode fromValue(int value) + { + ExceptionCode code = valueToCode.get(value); + if (code == null) + throw new ProtocolException(String.format("Unknown error code %d", value)); + return code; + } +} From e8a4336c554865f9252c860713b34e4abe0119ee Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 22:01:07 +0100 Subject: [PATCH 15/52] exceptions: Import TransportException --- exceptions/TransportException.java | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 exceptions/TransportException.java diff --git a/exceptions/TransportException.java b/exceptions/TransportException.java new file mode 100644 index 0000000000..70d1da57a3 --- /dev/null +++ b/exceptions/TransportException.java @@ -0,0 +1,31 @@ +/* + * 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. + */ +package org.apache.cassandra.exceptions; + +public interface TransportException +{ + /** + * The exception code. + */ + public ExceptionCode code(); + + /** + * The exception message. + */ + public String getMessage(); +} From 09c54d256d9b39307863cba7dbcbf5f6d80017d7 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 09:14:08 +0100 Subject: [PATCH 16/52] excpetions: Convert ExceptionCode --- exceptions/ExceptionCode.java | 71 ----------------------------------- exceptions/exceptions.hh | 23 ++++++++++++ 2 files changed, 23 insertions(+), 71 deletions(-) delete mode 100644 exceptions/ExceptionCode.java diff --git a/exceptions/ExceptionCode.java b/exceptions/ExceptionCode.java deleted file mode 100644 index ce082a79c0..0000000000 --- a/exceptions/ExceptionCode.java +++ /dev/null @@ -1,71 +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. - */ -package org.apache.cassandra.exceptions; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.cassandra.transport.ProtocolException; - -/** - * Exceptions code, as defined by the binary protocol. - */ -public enum ExceptionCode -{ - SERVER_ERROR (0x0000), - PROTOCOL_ERROR (0x000A), - - BAD_CREDENTIALS (0x0100), - - // 1xx: problem during request execution - UNAVAILABLE (0x1000), - OVERLOADED (0x1001), - IS_BOOTSTRAPPING(0x1002), - TRUNCATE_ERROR (0x1003), - WRITE_TIMEOUT (0x1100), - READ_TIMEOUT (0x1200), - - // 2xx: problem validating the request - SYNTAX_ERROR (0x2000), - UNAUTHORIZED (0x2100), - INVALID (0x2200), - CONFIG_ERROR (0x2300), - ALREADY_EXISTS (0x2400), - UNPREPARED (0x2500); - - public final int value; - private static final Map valueToCode = new HashMap(ExceptionCode.values().length); - static - { - for (ExceptionCode code : ExceptionCode.values()) - valueToCode.put(code.value, code); - } - - private ExceptionCode(int value) - { - this.value = value; - } - - public static ExceptionCode fromValue(int value) - { - ExceptionCode code = valueToCode.get(value); - if (code == null) - throw new ProtocolException(String.format("Unknown error code %d", value)); - return code; - } -} diff --git a/exceptions/exceptions.hh b/exceptions/exceptions.hh index 687e104f05..2e7a46728e 100644 --- a/exceptions/exceptions.hh +++ b/exceptions/exceptions.hh @@ -26,6 +26,29 @@ public: { } }; +enum exception_code { + SERVER_ERROR = 0x0000, + PROTOCOL_ERROR = 0x000A, + + BAD_CREDENTIALS = 0x0100, + + // 1xx: problem during request execution + UNAVAILABLE = 0x1000, + OVERLOADED = 0x1001, + IS_BOOTSTRAPPING= 0x1002, + TRUNCATE_ERROR = 0x1003, + WRITE_TIMEOUT = 0x1100, + READ_TIMEOUT = 0x1200, + + // 2xx: problem validating the request + SYNTAX_ERROR = 0x2000, + UNAUTHORIZED = 0x2100, + INVALID = 0x2200, + CONFIG_ERROR = 0x2300, + ALREADY_EXISTS = 0x2400, + UNPREPARED = 0x2500 +}; + } #endif From 54dbc5de256ed14cfb1e3ca1dbe66904f3d6cc24 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 09:15:28 +0100 Subject: [PATCH 17/52] exceptions: Import SyntaxException.java --- exceptions/SyntaxException.java | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 exceptions/SyntaxException.java diff --git a/exceptions/SyntaxException.java b/exceptions/SyntaxException.java new file mode 100644 index 0000000000..3a3db35c75 --- /dev/null +++ b/exceptions/SyntaxException.java @@ -0,0 +1,26 @@ +/* + * 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. + */ +package org.apache.cassandra.exceptions; + +public class SyntaxException extends RequestValidationException +{ + public SyntaxException(String msg) + { + super(ExceptionCode.SYNTAX_ERROR, msg); + } +} From 59803cce102e58f5309e497e450fdaedb7db73ba Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 09:15:43 +0100 Subject: [PATCH 18/52] exceptions: Import RequestValidationException.java --- exceptions/RequestValidationException.java | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 exceptions/RequestValidationException.java diff --git a/exceptions/RequestValidationException.java b/exceptions/RequestValidationException.java new file mode 100644 index 0000000000..a168274067 --- /dev/null +++ b/exceptions/RequestValidationException.java @@ -0,0 +1,31 @@ +/* + * 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. + */ +package org.apache.cassandra.exceptions; + +public abstract class RequestValidationException extends CassandraException +{ + protected RequestValidationException(ExceptionCode code, String msg) + { + super(code, msg); + } + + protected RequestValidationException(ExceptionCode code, String msg, Throwable e) + { + super(code, msg, e); + } +} From c27bdb652b38c98d9be30efd409cb6eff9d7056f Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 09:15:59 +0100 Subject: [PATCH 19/52] exceptions: Import CassandraException.java --- exceptions/CassandraException.java | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 exceptions/CassandraException.java diff --git a/exceptions/CassandraException.java b/exceptions/CassandraException.java new file mode 100644 index 0000000000..aaa8055ef1 --- /dev/null +++ b/exceptions/CassandraException.java @@ -0,0 +1,40 @@ +/* + * 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. + */ +package org.apache.cassandra.exceptions; + +public abstract class CassandraException extends Exception implements TransportException +{ + private final ExceptionCode code; + + protected CassandraException(ExceptionCode code, String msg) + { + super(msg); + this.code = code; + } + + protected CassandraException(ExceptionCode code, String msg, Throwable cause) + { + super(msg, cause); + this.code = code; + } + + public ExceptionCode code() + { + return code; + } +} From 6601e6a24fbc3e45e8368d27371b3a5a78be20ae Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 09:16:51 +0100 Subject: [PATCH 20/52] exceptions: Convert SyntaxException and its base classes --- exceptions/CassandraException.java | 40 ---------------------- exceptions/RequestValidationException.java | 31 ----------------- exceptions/SyntaxException.java | 26 -------------- exceptions/exceptions.hh | 33 ++++++++++++++++++ 4 files changed, 33 insertions(+), 97 deletions(-) delete mode 100644 exceptions/CassandraException.java delete mode 100644 exceptions/RequestValidationException.java delete mode 100644 exceptions/SyntaxException.java diff --git a/exceptions/CassandraException.java b/exceptions/CassandraException.java deleted file mode 100644 index aaa8055ef1..0000000000 --- a/exceptions/CassandraException.java +++ /dev/null @@ -1,40 +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. - */ -package org.apache.cassandra.exceptions; - -public abstract class CassandraException extends Exception implements TransportException -{ - private final ExceptionCode code; - - protected CassandraException(ExceptionCode code, String msg) - { - super(msg); - this.code = code; - } - - protected CassandraException(ExceptionCode code, String msg, Throwable cause) - { - super(msg, cause); - this.code = code; - } - - public ExceptionCode code() - { - return code; - } -} diff --git a/exceptions/RequestValidationException.java b/exceptions/RequestValidationException.java deleted file mode 100644 index a168274067..0000000000 --- a/exceptions/RequestValidationException.java +++ /dev/null @@ -1,31 +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. - */ -package org.apache.cassandra.exceptions; - -public abstract class RequestValidationException extends CassandraException -{ - protected RequestValidationException(ExceptionCode code, String msg) - { - super(code, msg); - } - - protected RequestValidationException(ExceptionCode code, String msg, Throwable e) - { - super(code, msg, e); - } -} diff --git a/exceptions/SyntaxException.java b/exceptions/SyntaxException.java deleted file mode 100644 index 3a3db35c75..0000000000 --- a/exceptions/SyntaxException.java +++ /dev/null @@ -1,26 +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. - */ -package org.apache.cassandra.exceptions; - -public class SyntaxException extends RequestValidationException -{ - public SyntaxException(String msg) - { - super(ExceptionCode.SYNTAX_ERROR, msg); - } -} diff --git a/exceptions/exceptions.hh b/exceptions/exceptions.hh index 2e7a46728e..614dbc195b 100644 --- a/exceptions/exceptions.hh +++ b/exceptions/exceptions.hh @@ -2,6 +2,7 @@ #define EXCEPTIONS_HH #include +#include "core/sstring.hh" namespace exceptions { @@ -49,6 +50,38 @@ enum exception_code { UNPREPARED = 0x2500 }; +class transport_exception { +public: + virtual exception_code code() const = 0; + virtual sstring get_message() const = 0; +}; + +class cassandra_exception : public std::exception, public transport_exception { +private: + exception_code _code; + sstring _msg; +public: + cassandra_exception(exception_code code, sstring msg) + : _code(code) + , _msg(std::move(msg)) + { } + virtual const char* what() const noexcept override { return _msg.begin(); } + virtual exception_code code() const override { return _code; } + virtual sstring get_message() const override { return what(); } +}; + +class request_validation_exception : public cassandra_exception { +public: + using cassandra_exception::cassandra_exception; +}; + +class syntax_exception : public request_validation_exception { +public: + syntax_exception(sstring msg) + : request_validation_exception(exception_code::SYNTAX_ERROR, std::move(msg)) + { } +}; + } #endif From b7e3c46a42e61666265e981ce370cd7c7916a1de Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 10:09:28 +0100 Subject: [PATCH 21/52] service: Import PagingState.java --- service/pager/PagingState.java | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 service/pager/PagingState.java diff --git a/service/pager/PagingState.java b/service/pager/PagingState.java new file mode 100644 index 0000000000..bbae921025 --- /dev/null +++ b/service/pager/PagingState.java @@ -0,0 +1,88 @@ +/* + * 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. + */ +package org.apache.cassandra.service.pager; + +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.cassandra.io.util.DataOutputBuffer; +import org.apache.cassandra.transport.ProtocolException; +import org.apache.cassandra.utils.ByteBufferUtil; + +public class PagingState +{ + public final ByteBuffer partitionKey; + public final ByteBuffer cellName; + public final int remaining; + + public PagingState(ByteBuffer partitionKey, ByteBuffer cellName, int remaining) + { + this.partitionKey = partitionKey == null ? ByteBufferUtil.EMPTY_BYTE_BUFFER : partitionKey; + this.cellName = cellName == null ? ByteBufferUtil.EMPTY_BYTE_BUFFER : cellName; + this.remaining = remaining; + } + + public static PagingState deserialize(ByteBuffer bytes) + { + if (bytes == null) + return null; + + try + { + DataInputStream in = new DataInputStream(ByteBufferUtil.inputStream(bytes)); + ByteBuffer pk = ByteBufferUtil.readWithShortLength(in); + ByteBuffer cn = ByteBufferUtil.readWithShortLength(in); + int remaining = in.readInt(); + return new PagingState(pk, cn, remaining); + } + catch (IOException e) + { + throw new ProtocolException("Invalid value for the paging state"); + } + } + + public ByteBuffer serialize() + { + try + { + DataOutputBuffer out = new DataOutputBuffer(serializedSize()); + ByteBufferUtil.writeWithShortLength(partitionKey, out); + ByteBufferUtil.writeWithShortLength(cellName, out); + out.writeInt(remaining); + return out.asByteBuffer(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + private int serializedSize() + { + return 2 + partitionKey.remaining() + + 2 + cellName.remaining() + + 4; + } + + @Override + public String toString() + { + return String.format("PagingState(key=%s, cellname=%s, remaining=%d", ByteBufferUtil.bytesToHex(partitionKey), ByteBufferUtil.bytesToHex(cellName), remaining); + } +} From 7c7c23b5b6084aededb10bce650f812b86f10c32 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 10:11:31 +0100 Subject: [PATCH 22/52] service: Stub PagingState --- .../{PagingState.java => paging_state.hh} | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) rename service/pager/{PagingState.java => paging_state.hh} (89%) diff --git a/service/pager/PagingState.java b/service/pager/paging_state.hh similarity index 89% rename from service/pager/PagingState.java rename to service/pager/paging_state.hh index bbae921025..8857e8392c 100644 --- a/service/pager/PagingState.java +++ b/service/pager/paging_state.hh @@ -15,18 +15,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.cassandra.service.pager; -import java.io.DataInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; +/* + * Copyright 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ -import org.apache.cassandra.io.util.DataOutputBuffer; -import org.apache.cassandra.transport.ProtocolException; -import org.apache.cassandra.utils.ByteBufferUtil; +#pragma once -public class PagingState -{ +namespace service { + +namespace pager { + +class paging_state final { +#if 0 public final ByteBuffer partitionKey; public final ByteBuffer cellName; public final int remaining; @@ -85,4 +88,9 @@ public class PagingState { return String.format("PagingState(key=%s, cellname=%s, remaining=%d", ByteBufferUtil.bytesToHex(partitionKey), ByteBufferUtil.bytesToHex(cellName), remaining); } +#endif +}; + +} + } From 5359200ee435a3e0b97583b43196edc9e65730eb Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 14:22:55 +0100 Subject: [PATCH 23/52] cql3: Use @context instead of @members in Cql.g There is a difference in meaning of @members between Cpp and Java targets, because of reasons which are not clear to me. In Cpp @members section is not put inside parser/lexer class definition, so any varaible definitions declared there become program-global variables instead of instance-local. We should use @context instead. --- cql3/Cql.g | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cql3/Cql.g b/cql3/Cql.g index ea3d5af71b..c420c5a423 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -76,7 +76,7 @@ using cql3::native_cql3_type; #endif } -@members { +@context { #if 0 private final List listeners = new ArrayList(); private final List bindVariables = new ArrayList(); @@ -212,7 +212,7 @@ using cql3::native_cql3_type; #endif } -@lexer::members { +@lexer::context { #if 0 List tokens = new ArrayList(); From 43e693e72d00d8c64889dfb2ceb8ce33755710b2 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:09:17 +0100 Subject: [PATCH 24/52] service: Implement client_state::get_timeout() --- service/client_state.hh | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/service/client_state.hh b/service/client_state.hh index c1f3583287..8e78e70c4a 100644 --- a/service/client_state.hh +++ b/service/client_state.hh @@ -26,6 +26,7 @@ #include "exceptions/exceptions.hh" #include "unimplemented.hh" +#include "database.hh" namespace service { @@ -84,10 +85,12 @@ private: // The remote address of the client - null for internal clients. private final SocketAddress remoteAddress; +#endif // The biggest timestamp that was returned by getTimestamp/assigned to a query - private final AtomicLong lastTimestampMicros = new AtomicLong(0); + api::timestamp_type _last_timestamp_micros = 0; +#if 0 /** * Construct a new, empty ClientState for internal calls. */ @@ -120,23 +123,22 @@ private: { return new ClientState(remoteAddress); } +#endif /** * This clock guarantees that updates for the same ClientState will be ordered * in the sequence seen, even if multiple updates happen in the same millisecond. */ - public long getTimestamp() - { - while (true) - { - long current = System.currentTimeMillis() * 1000; - long last = lastTimestampMicros.get(); - long tstamp = last >= current ? last + 1 : current; - if (lastTimestampMicros.compareAndSet(last, tstamp)) - return tstamp; - } +public: + api::timestamp_type get_timestamp() { + auto current = db_clock::now().time_since_epoch().count() * 1000; + auto last = _last_timestamp_micros; + auto result = last >= current ? last + 1 : current; + _last_timestamp_micros = result; + return result; } +#if 0 /** * Can be use when a timestamp has been assigned by a query, but that timestamp is * not directly one returned by getTimestamp() (see SP.beginAndRepairPaxos()). From 0b39d284b81423d67e937c565e562b83032da27e Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:09:36 +0100 Subject: [PATCH 25/52] service: Return keyspace name by const& It should be up to the caller if copy is needed. --- service/client_state.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service/client_state.hh b/service/client_state.hh index 8e78e70c4a..5e4c8efecb 100644 --- a/service/client_state.hh +++ b/service/client_state.hh @@ -183,7 +183,7 @@ public: _keyspace = keyspace; } - sstring get_keyspace() const { + const sstring& get_keyspace() const { if (_keyspace.empty()) { throw exceptions::invalid_request_exception("No keyspace has been specified. USE a keyspace, or explicitly specify keyspace.tablename"); } From d6fcb92dd3d89512d9f2857e0e515da3508ae036 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:12:11 +0100 Subject: [PATCH 26/52] cql3: Add missing set_bound_variables() call Resulted in nullptr dereference. --- cql3/Cql.g | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cql3/Cql.g b/cql3/Cql.g index c420c5a423..f17eebfe01 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -79,8 +79,11 @@ using cql3::native_cql3_type; @context { #if 0 private final List listeners = new ArrayList(); - private final List bindVariables = new ArrayList(); +#endif +std::vector<::shared_ptr> _bind_variables; + +#if 0 public static final Set reservedTypeNames = new HashSet() {{ add("byte"); @@ -257,9 +260,7 @@ query returns [shared_ptr stmnt] ; cqlStatement returns [shared_ptr stmt] -#if 0 - @after{ if (stmt != null) stmt.setBoundVariables(bindVariables); } -#endif + @after{ if (stmt) { stmt->set_bound_variables(_bind_variables); } } : st1= selectStatement { $stmt = st1; } | st2= insertStatement { $stmt = st2; } #if 0 From ca4688540f1efad043d5b1a68896ed1cee46e1c8 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:15:41 +0100 Subject: [PATCH 27/52] cql3: Drop redundant optional<> around shared_ptr<> --- cql3/attributes.hh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cql3/attributes.hh b/cql3/attributes.hh index 6ca42c0a1c..6bfccca929 100644 --- a/cql3/attributes.hh +++ b/cql3/attributes.hh @@ -38,24 +38,24 @@ namespace cql3 { */ class attributes final { private: - const std::experimental::optional<::shared_ptr> _timestamp; - const std::experimental::optional<::shared_ptr> _time_to_live; + const ::shared_ptr _timestamp; + const ::shared_ptr _time_to_live; public: static std::unique_ptr none() { - return std::unique_ptr{new attributes{std::move(std::experimental::optional<::shared_ptr>{}), std::move(std::experimental::optional<::shared_ptr>{})}}; + return std::unique_ptr{new attributes{{}, {}}}; } private: - attributes(std::experimental::optional<::shared_ptr>&& timestamp, std::experimental::optional<::shared_ptr>&& time_to_live) + attributes(::shared_ptr&& timestamp, ::shared_ptr&& time_to_live) : _timestamp{std::move(timestamp)} , _time_to_live{std::move(time_to_live)} { } public: bool uses_function(const sstring& ks_name, const sstring& function_name) const { - return (_timestamp && _timestamp.value()->uses_function(ks_name, function_name)) - || (_time_to_live && _time_to_live.value()->uses_function(ks_name, function_name)); + return (_timestamp && _timestamp->uses_function(ks_name, function_name)) + || (_time_to_live && _time_to_live->uses_function(ks_name, function_name)); } bool is_timestamp_set() const { @@ -71,7 +71,7 @@ public: return now; } - bytes_opt tval = _timestamp.value()->bind_and_get(options); + bytes_opt tval = _timestamp->bind_and_get(options); if (!tval) { throw exceptions::invalid_request_exception("Invalid null value of timestamp"); } @@ -88,7 +88,7 @@ public: if (!_time_to_live) return 0; - bytes_opt tval = _time_to_live.value()->bind_and_get(options); + bytes_opt tval = _time_to_live->bind_and_get(options); if (!tval) { throw exceptions::invalid_request_exception("Invalid null value of TTL"); } @@ -114,10 +114,10 @@ public: void collect_marker_specification(::shared_ptr bound_names) { if (_timestamp) { - _timestamp.value()->collect_marker_specification(bound_names); + _timestamp->collect_marker_specification(bound_names); } if (_time_to_live) { - _time_to_live.value()->collect_marker_specification(bound_names); + _time_to_live->collect_marker_specification(bound_names); } } From 0973d7df7c7eebc0a67a78df0cad11ed3542aaeb Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:16:35 +0100 Subject: [PATCH 28/52] cql3: Avoid unnecessary copy of a string --- cql3/cf_name.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cql3/cf_name.hh b/cql3/cf_name.hh index 6b15e31074..76df0b4879 100644 --- a/cql3/cf_name.hh +++ b/cql3/cf_name.hh @@ -39,7 +39,7 @@ public: if (!keep_case) { std::transform(ks.begin(), ks.end(), ks.begin(), ::tolower); } - _ks_name = std::experimental::make_optional(ks); + _ks_name = std::experimental::make_optional(std::move(ks)); } void set_column_family(sstring cf, bool keep_case) { From 0f2a1dffb6d477dcfd1df6f09186dac4df5e57c6 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:19:27 +0100 Subject: [PATCH 29/52] cql3: Pass bound term count around as unsigned integer It's compared with size_t and is set from size_t. To avoid ugly casts we can store it as unsigned int. It's always positive anyway. Origin uses signed int ("int") because there is no unsigned int in Java. --- cql3/cql_statement.hh | 2 +- cql3/statements/delete_statement.hh | 2 +- cql3/statements/modification_statement.hh | 6 +++--- cql3/statements/schema_altering_statement.hh | 2 +- cql3/statements/truncate_statement.hh | 2 +- cql3/statements/update_statement.hh | 2 +- cql3/statements/use_statement.hh | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cql3/cql_statement.hh b/cql3/cql_statement.hh index 56678cf532..f67e66a7a7 100644 --- a/cql3/cql_statement.hh +++ b/cql3/cql_statement.hh @@ -39,7 +39,7 @@ public: virtual ~cql_statement() { } - virtual int get_bound_terms() = 0; + virtual uint32_t get_bound_terms() = 0; /** * Perform any access verification necessary for the statement. diff --git a/cql3/statements/delete_statement.hh b/cql3/statements/delete_statement.hh index 975bd0e75c..f9db936075 100644 --- a/cql3/statements/delete_statement.hh +++ b/cql3/statements/delete_statement.hh @@ -54,7 +54,7 @@ import org.apache.cassandra.utils.Pair; */ class delete_statement : public modification_statement { public: - delete_statement(statement_type type, int32_t bound_terms, schema_ptr s, std::unique_ptr attrs) + delete_statement(statement_type type, uint32_t bound_terms, schema_ptr s, std::unique_ptr attrs) : modification_statement{type, bound_terms, std::move(s), std::move(attrs)} { } diff --git a/cql3/statements/modification_statement.hh b/cql3/statements/modification_statement.hh index 13acdad485..7caf3e6fc4 100644 --- a/cql3/statements/modification_statement.hh +++ b/cql3/statements/modification_statement.hh @@ -94,7 +94,7 @@ public: const statement_type type; private: - const int32_t _bound_terms; + const uint32_t _bound_terms; public: const schema_ptr s; @@ -122,7 +122,7 @@ private: }; public: - modification_statement(statement_type type_, int32_t bound_terms, schema_ptr schema_, std::unique_ptr attrs_) + modification_statement(statement_type type_, uint32_t bound_terms, schema_ptr schema_, std::unique_ptr attrs_) : type{type_} , _bound_terms{bound_terms} , s{schema_} @@ -162,7 +162,7 @@ public: virtual void add_update_for_key(mutation& m, const clustering_prefix& prefix, const update_parameters& params) = 0; - virtual int get_bound_terms() override { + virtual uint32_t get_bound_terms() override { return _bound_terms; } diff --git a/cql3/statements/schema_altering_statement.hh b/cql3/statements/schema_altering_statement.hh index 51ed67f5cd..89c7f0a558 100644 --- a/cql3/statements/schema_altering_statement.hh +++ b/cql3/statements/schema_altering_statement.hh @@ -55,7 +55,7 @@ protected: { } - virtual int get_bound_terms() override { + virtual uint32_t get_bound_terms() override { return 0; } diff --git a/cql3/statements/truncate_statement.hh b/cql3/statements/truncate_statement.hh index 7e475ed695..979814540f 100644 --- a/cql3/statements/truncate_statement.hh +++ b/cql3/statements/truncate_statement.hh @@ -40,7 +40,7 @@ public: : cf_statement{std::move(name)} { } - virtual int get_bound_terms() override { + virtual uint32_t get_bound_terms() override { return 0; } diff --git a/cql3/statements/update_statement.hh b/cql3/statements/update_statement.hh index af11f8134b..4b91f90f10 100644 --- a/cql3/statements/update_statement.hh +++ b/cql3/statements/update_statement.hh @@ -65,7 +65,7 @@ public: private static final Constants.Value EMPTY = new Constants.Value(ByteBufferUtil.EMPTY_BYTE_BUFFER); #endif - update_statement(statement_type type, int32_t bound_terms, schema_ptr s, std::unique_ptr attrs) + update_statement(statement_type type, uint32_t bound_terms, schema_ptr s, std::unique_ptr attrs) : modification_statement{type, bound_terms, std::move(s), std::move(attrs)} { } diff --git a/cql3/statements/use_statement.hh b/cql3/statements/use_statement.hh index e48834bf07..abb34a97bb 100644 --- a/cql3/statements/use_statement.hh +++ b/cql3/statements/use_statement.hh @@ -41,7 +41,7 @@ public: : _keyspace(keyspace) { } - virtual int get_bound_terms() override { + virtual uint32_t get_bound_terms() override { return 0; } From 9a307918ef610f1a43dccabfa00675a9cec2175a Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:23:12 +0100 Subject: [PATCH 30/52] db: Cleanup comment --- database.hh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/database.hh b/database.hh index 7fbb0430b3..c7c8fb1e6b 100644 --- a/database.hh +++ b/database.hh @@ -48,10 +48,9 @@ timestamp_type constexpr max_timestamp = std::numeric_limits::ma } /** -* Represents deletion operation. Can be commuted with other tombstones via apply() method. -* Can be empty. -* -*/ + * Represents deletion operation. Can be commuted with other tombstones via apply() method. + * Can be empty. + */ struct tombstone final { api::timestamp_type timestamp; gc_clock::time_point ttl; From b3544238d1ea784734eec7ed20dd6fd42a4cdf61 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:26:21 +0100 Subject: [PATCH 31/52] service: Implement query_state:: get_client_state() and get_timestamp() Needed by query_options and query_processor. --- service/query_state.hh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/service/query_state.hh b/service/query_state.hh index acb665cbc5..91b615ef21 100644 --- a/service/query_state.hh +++ b/service/query_state.hh @@ -1,10 +1,21 @@ #ifndef SERVICE_QUERY_STATE_HH #define SERVICE_QUERY_STATE_HH +#include "service/client_state.hh" + namespace service { -class query_state { - // FIXME: stub +class query_state final { +private: + client_state& _client_state; +public: + query_state(client_state& client_state_) : _client_state(client_state_) {} + client_state& get_client_state() const { + return _client_state; + } + api::timestamp_type get_timestamp() { + return _client_state.get_timestamp(); + } }; } From 3aea084f2b5d7ed072f7e620784da320714c4ec8 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:53:54 +0100 Subject: [PATCH 32/52] types: Add sstring_view type alias --- types.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/types.hh b/types.hh index 839b2f0233..1d48fc56ce 100644 --- a/types.hh +++ b/types.hh @@ -20,6 +20,7 @@ using bytes = basic_sstring; using bytes_view = std::experimental::string_view; using bytes_opt = std::experimental::optional; +using sstring_view = std::experimental::string_view; sstring to_hex(const bytes& b); sstring to_hex(const bytes_opt& b); From 863d305a6609d407b353b54408260cec0651ddda Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 17:32:49 +0100 Subject: [PATCH 33/52] types: Make from_string() work on sstring_view --- tuple.hh | 2 +- types.cc | 20 ++++++++++---------- types.hh | 8 +++++++- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/tuple.hh b/tuple.hh index 33e4c7444f..2ab3a2fad7 100644 --- a/tuple.hh +++ b/tuple.hh @@ -162,7 +162,7 @@ public: // TODO: make the length byte-order comparable by adding numeric_limits::min() when serializing return false; } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) override { diff --git a/types.cc b/types.cc index 82ad5fec09..82e7504ab9 100644 --- a/types.cc +++ b/types.cc @@ -66,14 +66,14 @@ struct int32_type_impl : simple_type_impl { *reinterpret_cast(b.begin()) = (int32_t)net::hton((uint32_t)v); return b; } - int32_t parse_int(const sstring& s) { + int32_t parse_int(sstring_view s) { try { - return boost::lexical_cast(s); + return boost::lexical_cast(s.begin(), s.size()); } catch (const boost::bad_lexical_cast& e) { throw marshal_exception(sprint("Invalid number format '%s'", s)); } } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { return decompose_value(parse_int(s)); } virtual sstring to_string(const bytes& b) override { @@ -103,7 +103,7 @@ struct long_type_impl : simple_type_impl { auto v = int64_t(net::ntoh(u)); return boost::any(v); } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) override { @@ -135,7 +135,7 @@ struct string_type_impl : public abstract_type { virtual size_t hash(const bytes& v) override { return std::hash()(v); } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { return to_bytes(s); } virtual sstring to_string(const bytes& b) override { @@ -166,7 +166,7 @@ struct bytes_type_impl : public abstract_type { virtual size_t hash(const bytes& v) override { return std::hash()(v); } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) override { @@ -189,7 +189,7 @@ struct boolean_type_impl : public simple_type_impl { } return boost::any(tmp != 0); } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) override { @@ -226,7 +226,7 @@ struct date_type_impl : public abstract_type { virtual size_t hash(const bytes& v) override { return std::hash()(v); } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) override { @@ -272,7 +272,7 @@ struct timeuuid_type_impl : public abstract_type { virtual size_t hash(const bytes& v) override { return std::hash()(v); } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) override { @@ -315,7 +315,7 @@ struct timestamp_type_impl : simple_type_impl { return boost::any(db_clock::time_point(db_clock::duration(net::ntoh(v)))); } // FIXME: isCompatibleWith(timestampuuid) - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) override { diff --git a/types.hh b/types.hh index 1d48fc56ce..3b0adc71cc 100644 --- a/types.hh +++ b/types.hh @@ -114,7 +114,7 @@ public: return to_string(b); } virtual sstring to_string(const bytes& b) = 0; - virtual bytes from_string(const sstring& text) = 0; + virtual bytes from_string(sstring_view text) = 0; virtual bool is_counter() { return false; } virtual bool is_collection() { return false; } virtual bool is_multi_cell() { return false; } @@ -194,6 +194,12 @@ to_bytes(const std::string& x) { return bytes(reinterpret_cast(x.data()), x.size()); } +inline +bytes +to_bytes(sstring_view x) { + return bytes(x.begin(), x.size()); +} + inline bytes to_bytes(const sstring& x) { From 63d152ece83d13e1ccf7e814f9dea4320e96ded4 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 17:33:45 +0100 Subject: [PATCH 34/52] types: Introduce abstract_type::as_cql3_type() Origin also has it. --- tuple.hh | 3 +++ types.cc | 40 +++++++++++++++++++++++++++++++++++----- types.hh | 7 +++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/tuple.hh b/tuple.hh index 2ab3a2fad7..0e8a95e4cf 100644 --- a/tuple.hh +++ b/tuple.hh @@ -214,6 +214,9 @@ public: } return true; } + virtual ::shared_ptr as_cql3_type() override { + assert(0); + } }; using tuple_prefix = tuple_type; diff --git a/types.cc b/types.cc index 82e7504ab9..9c5cca7744 100644 --- a/types.cc +++ b/types.cc @@ -3,6 +3,7 @@ */ #include +#include "cql3/cql3_type.hh" #include "types.hh" template @@ -82,6 +83,9 @@ struct int32_type_impl : simple_type_impl { } return to_sstring(compose_value(b)); } + virtual ::shared_ptr as_cql3_type() override { + return cql3::native_cql3_type::int_; + } }; struct long_type_impl : simple_type_impl { @@ -109,10 +113,14 @@ struct long_type_impl : simple_type_impl { virtual sstring to_string(const bytes& b) override { throw std::runtime_error("not implemented"); } + virtual ::shared_ptr as_cql3_type() override { + return cql3::native_cql3_type::bigint; + } }; struct string_type_impl : public abstract_type { - string_type_impl(sstring name) : abstract_type(name) {} + string_type_impl(sstring name, shared_ptr cql3_type) + : abstract_type(name), _cql3_type(cql3_type) {} virtual void serialize(const boost::any& value, std::ostream& out) override { auto& v = boost::any_cast(value); out.write(v.c_str(), v.size()); @@ -141,9 +149,13 @@ struct string_type_impl : public abstract_type { virtual sstring to_string(const bytes& b) override { return sstring(b); } + virtual ::shared_ptr as_cql3_type() override { + return _cql3_type; + } + shared_ptr _cql3_type; }; -struct bytes_type_impl : public abstract_type { +struct bytes_type_impl final : public abstract_type { bytes_type_impl() : abstract_type("bytes") {} virtual void serialize(const boost::any& value, std::ostream& out) override { auto& v = boost::any_cast(value); @@ -172,6 +184,9 @@ struct bytes_type_impl : public abstract_type { virtual sstring to_string(const bytes& b) override { throw std::runtime_error("not implemented"); } + virtual ::shared_ptr as_cql3_type() override { + return cql3::native_cql3_type::blob; + } }; struct boolean_type_impl : public simple_type_impl { @@ -195,6 +210,9 @@ struct boolean_type_impl : public simple_type_impl { virtual sstring to_string(const bytes& b) override { throw std::runtime_error("not implemented"); } + virtual ::shared_ptr as_cql3_type() override { + return cql3::native_cql3_type::boolean; + } }; struct date_type_impl : public abstract_type { @@ -232,6 +250,9 @@ struct date_type_impl : public abstract_type { virtual sstring to_string(const bytes& b) override { throw std::runtime_error("not implemented"); } + virtual ::shared_ptr as_cql3_type() override { + return cql3::native_cql3_type::timestamp; + } }; struct timeuuid_type_impl : public abstract_type { @@ -278,6 +299,9 @@ struct timeuuid_type_impl : public abstract_type { virtual sstring to_string(const bytes& b) override { throw std::runtime_error("not implemented"); } + virtual ::shared_ptr as_cql3_type() override { + return cql3::native_cql3_type::timeuuid; + } private: static int compare_bytes(const bytes& o1, const bytes& o2) { auto compare_pos = [&] (unsigned pos, int mask, int ifequal) { @@ -321,6 +345,9 @@ struct timestamp_type_impl : simple_type_impl { virtual sstring to_string(const bytes& b) override { throw std::runtime_error("not implemented"); } + virtual ::shared_ptr as_cql3_type() override { + return cql3::native_cql3_type::timestamp; + } }; struct uuid_type_impl : abstract_type { @@ -372,19 +399,22 @@ struct uuid_type_impl : abstract_type { virtual size_t hash(const bytes& v) override { return std::hash()(v); } - virtual bytes from_string(const sstring& s) override { + virtual bytes from_string(sstring_view s) override { throw std::runtime_error("not implemented"); } virtual sstring to_string(const bytes& b) override { throw std::runtime_error("not implemented"); } + virtual ::shared_ptr as_cql3_type() override { + return cql3::native_cql3_type::uuid; + } }; thread_local shared_ptr int32_type(make_shared()); thread_local shared_ptr long_type(make_shared()); -thread_local shared_ptr ascii_type(make_shared("ascii")); +thread_local shared_ptr ascii_type(make_shared("ascii", cql3::native_cql3_type::ascii)); thread_local shared_ptr bytes_type(make_shared()); -thread_local shared_ptr utf8_type(make_shared("utf8")); +thread_local shared_ptr utf8_type(make_shared("utf8", cql3::native_cql3_type::text)); thread_local shared_ptr boolean_type(make_shared()); thread_local shared_ptr date_type(make_shared()); thread_local shared_ptr timeuuid_type(make_shared()); diff --git a/types.hh b/types.hh index 3b0adc71cc..c9b2e93e0b 100644 --- a/types.hh +++ b/types.hh @@ -16,6 +16,12 @@ #include "net/byteorder.hh" #include "db_clock.hh" +namespace cql3 { + +class cql3_type; + +} + // FIXME: should be int8_t using bytes = basic_sstring; using bytes_view = std::experimental::string_view; @@ -118,6 +124,7 @@ public: virtual bool is_counter() { return false; } virtual bool is_collection() { return false; } virtual bool is_multi_cell() { return false; } + virtual ::shared_ptr as_cql3_type() = 0; protected: template > bool default_less(const bytes& b1, const bytes& b2, Compare compare = Compare()); From 0c5e2da0e0b13abeeddc12d10b2f5d564cbf77eb Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Tue, 10 Feb 2015 16:27:46 +0100 Subject: [PATCH 35/52] cql3: Convert more of query_options Needed by query_processor. --- configure.py | 1 + cql3/query_options.cc | 34 +++++++++ cql3/query_options.hh | 160 +++++++++++++++++------------------------- 3 files changed, 99 insertions(+), 96 deletions(-) create mode 100644 cql3/query_options.cc diff --git a/configure.py b/configure.py index 8da75fb6af..50660a29e2 100755 --- a/configure.py +++ b/configure.py @@ -249,6 +249,7 @@ urchin_core = (['database.cc', 'cql3/relation.cc', 'cql3/column_identifier.cc', 'cql3/constants.cc', + 'cql3/query_options.cc', 'db/db.cc', 'io/io.cc', 'utils/utils.cc', diff --git a/cql3/query_options.cc b/cql3/query_options.cc new file mode 100644 index 0000000000..b6488aaf03 --- /dev/null +++ b/cql3/query_options.cc @@ -0,0 +1,34 @@ +/* + * 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 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ + +#include "query_options.hh" + +namespace cql3 { + +const query_options::specific_options query_options::specific_options::DEFAULT{-1, {}, {}, api::missing_timestamp}; + +default_query_options query_options::DEFAULT{db::consistency_level::ONE, + {}, false, query_options::specific_options::DEFAULT, 3}; + +} diff --git a/cql3/query_options.hh b/cql3/query_options.hh index 01f2106628..321f3dd3ca 100644 --- a/cql3/query_options.hh +++ b/cql3/query_options.hh @@ -28,21 +28,34 @@ #include "database.hh" #include "db/consistency_level.hh" #include "service/query_state.hh" +#include "service/pager/paging_state.hh" +#include "cql3/column_specification.hh" namespace cql3 { +class default_query_options; + /** * Options for a query. */ class query_options { public: -#if 0 - public static final QueryOptions DEFAULT = new DefaultQueryOptions(ConsistencyLevel.ONE, - Collections.emptyList(), - false, - SpecificOptions.DEFAULT, - 3); + // Options that are likely to not be present in most queries + struct specific_options final { + static const specific_options DEFAULT; + const int page_size; + const ::shared_ptr state; + const std::experimental::optional serial_consistency; + const api::timestamp_type timestamp; + }; + + // It can't be const because of prepare() + static default_query_options DEFAULT; + + virtual ~query_options() {} + +#if 0 public static final CBCodec codec = new Codec(); public static QueryOptions fromProtocolV1(ConsistencyLevel consistency, List values) @@ -76,39 +89,26 @@ public: } #endif -public: virtual db::consistency_level get_consistency() const = 0; - -#if 0 - public abstract List getValues(); - public abstract boolean skipMetadata(); + virtual const std::vector& get_values() const = 0; + virtual bool skip_metadata() const = 0; /** The pageSize for this query. Will be <= 0 if not relevant for the query. */ - public int getPageSize() - { - return getSpecificOptions().pageSize; - } + int32_t get_page_size() const { return get_specific_options().page_size; } /** The paging state for this query, or null if not relevant. */ - public PagingState getPagingState() - { - return getSpecificOptions().state; + ::shared_ptr get_paging_state() const { + return get_specific_options().state; } /** Serial consistency for conditional updates. */ - public ConsistencyLevel getSerialConsistency() - { - return getSpecificOptions().serialConsistency; + std::experimental::optional get_serial_consistency() const { + return get_specific_options().serial_consistency; } -#endif -public: - api::timestamp_type get_timestamp(const service::query_state& state) const { - throw std::runtime_error("NOT IMPLEMENTED"); -#if 0 + api::timestamp_type get_timestamp(service::query_state& state) const { auto tstamp = get_specific_options().timestamp; return tstamp != api::missing_timestamp ? tstamp : state.get_timestamp(); -#endif } /** @@ -117,60 +117,14 @@ public: */ virtual int get_protocol_version() const = 0; -#if 0 // Mainly for the sake of BatchQueryOptions - abstract SpecificOptions getSpecificOptions(); + virtual const specific_options& get_specific_options() const = 0; - public QueryOptions prepare(List specs) - { - return this; - } - - static class DefaultQueryOptions extends QueryOptions - { - private final ConsistencyLevel consistency; - private final List values; - private final boolean skipMetadata; - - private final SpecificOptions options; - - private final transient int protocolVersion; - - DefaultQueryOptions(ConsistencyLevel consistency, List values, boolean skipMetadata, SpecificOptions options, int protocolVersion) - { - this.consistency = consistency; - this.values = values; - this.skipMetadata = skipMetadata; - this.options = options; - this.protocolVersion = protocolVersion; - } - - public ConsistencyLevel getConsistency() - { - return consistency; - } - - public List getValues() - { - return values; - } - - public boolean skipMetadata() - { - return skipMetadata; - } - - public int getProtocolVersion() - { - return protocolVersion; - } - - SpecificOptions getSpecificOptions() - { - return options; - } + query_options& prepare(const std::vector<::shared_ptr>& specs) { + return *this; } +#if 0 static abstract class QueryOptionsWrapper extends QueryOptions { protected final QueryOptions wrapped; @@ -247,25 +201,6 @@ public: } } - // Options that are likely to not be present in most queries - static class SpecificOptions - { - private static final SpecificOptions DEFAULT = new SpecificOptions(-1, null, null, Long.MIN_VALUE); - - private final int pageSize; - private final PagingState state; - private final ConsistencyLevel serialConsistency; - private final long timestamp; - - private SpecificOptions(int pageSize, PagingState state, ConsistencyLevel serialConsistency, long timestamp) - { - this.pageSize = pageSize; - this.state = state; - this.serialConsistency = serialConsistency == null ? ConsistencyLevel.SERIAL : serialConsistency; - this.timestamp = timestamp; - } - } - private static class Codec implements CBCodec { private static enum Flag @@ -418,6 +353,39 @@ public: #endif }; +class default_query_options : public query_options { +private: + const db::consistency_level _consistency; + const std::vector _values; + const bool _skip_metadata; + const specific_options _options; + const int32_t _protocol_version; // transient +public: + default_query_options(db::consistency_level consistency, std::vector values, bool skip_metadata, specific_options options, + int protocol_version) + : _consistency(consistency) + , _values(std::move(values)) + , _skip_metadata(skip_metadata) + , _options(std::move(options)) + , _protocol_version(protocol_version) + { } + virtual db::consistency_level get_consistency() const override { + return _consistency; + } + virtual const std::vector& get_values() const override { + return _values; + } + virtual bool skip_metadata() const override { + return _skip_metadata; + } + virtual int32_t get_protocol_version() const override { + return _protocol_version; + } + virtual const specific_options& get_specific_options() const override { + return _options; + } +}; + } #endif From 42049e067146c01eb13a42962821206b72759b52 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 09:28:49 +0100 Subject: [PATCH 36/52] cql3: Convert ErrorListener and ErrorCollector --- ...ErrorCollector.java => error_collector.hh} | 71 ++++++++++--------- .../{ErrorListener.java => error_listener.hh} | 26 +++++-- 2 files changed, 55 insertions(+), 42 deletions(-) rename cql3/{ErrorCollector.java => error_collector.hh} (88%) rename cql3/{ErrorListener.java => error_listener.hh} (75%) diff --git a/cql3/ErrorCollector.java b/cql3/error_collector.hh similarity index 88% rename from cql3/ErrorCollector.java rename to cql3/error_collector.hh index f49cca41d1..3ded8ecbf2 100644 --- a/cql3/ErrorCollector.java +++ b/cql3/error_collector.hh @@ -15,41 +15,46 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.cassandra.cql3; -import java.util.LinkedList; +/* + * Copyright 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ -import org.antlr.runtime.BaseRecognizer; -import org.antlr.runtime.Parser; -import org.antlr.runtime.RecognitionException; -import org.antlr.runtime.Token; -import org.antlr.runtime.TokenStream; -import org.apache.cassandra.exceptions.SyntaxException; +#pragma once + +#include "cql3/error_listener.hh" +#include "exceptions/exceptions.hh" +#include "types.hh" + +namespace cql3 { /** * ErrorListener that collect and enhance the errors send by the CQL lexer and parser. */ -public final class ErrorCollector implements ErrorListener -{ +template +class error_collector : public error_listener { /** * The offset of the first token of the snippet. */ - private static final int FIRST_TOKEN_OFFSET = 10; + static const int32_t FIRST_TOKEN_OFFSET = 10; /** * The offset of the last token of the snippet. */ - private static final int LAST_TOKEN_OFFSET = 2; + static const int32_t LAST_TOKEN_OFFSET = 2; /** * The CQL query. */ - private final String query; + const sstring_view _query; /** * The error messages. */ - private final LinkedList errorMsgs = new LinkedList<>(); + std::vector _error_msgs; +public: /** * Creates a new ErrorCollector instance to collect the syntax errors associated to the specified CQL @@ -57,17 +62,12 @@ public final class ErrorCollector implements ErrorListener * * @param query the CQL query that will be parsed */ - public ErrorCollector(String query) - { - this.query = query; - } + error_collector(const sstring_view& query) : _query(query) {} - /** - * {@inheritDoc} - */ - @Override - public void syntaxError(BaseRecognizer recognizer, String[] tokenNames, RecognitionException e) - { + virtual void syntax_error(Recognizer& recognizer, const std::vector& token_names) override { + // FIXME: stub + syntax_error(recognizer, "Parsing failed, detailed description construction not implemented yet"); +#if 0 String hdr = recognizer.getErrorHeader(e); String msg = recognizer.getErrorMessage(e, tokenNames); @@ -79,15 +79,11 @@ public final class ErrorCollector implements ErrorListener appendQuerySnippet((Parser) recognizer, builder); errorMsgs.add(builder.toString()); +#endif } - /** - * {@inheritDoc} - */ - @Override - public void syntaxError(BaseRecognizer recognizer, String errorMsg) - { - errorMsgs.add(errorMsg); + virtual void syntax_error(Recognizer& recognizer, const sstring& msg) override { + _error_msgs.emplace_back(msg); } /** @@ -95,12 +91,14 @@ public final class ErrorCollector implements ErrorListener * * @throws SyntaxException the syntax error. */ - public void throwFirstSyntaxError() throws SyntaxException - { - if (!errorMsgs.isEmpty()) - throw new SyntaxException(errorMsgs.getFirst()); + void throw_first_syntax_error() { + if (!_error_msgs.empty()) { + throw exceptions::syntax_exception(_error_msgs[0]); + } } +#if 0 + /** * Appends a query snippet to the message to help the user to understand the problem. * @@ -287,4 +285,7 @@ public final class ErrorCollector implements ErrorListener { return Math.max(0, index - FIRST_TOKEN_OFFSET); } +#endif +}; + } diff --git a/cql3/ErrorListener.java b/cql3/error_listener.hh similarity index 75% rename from cql3/ErrorListener.java rename to cql3/error_listener.hh index 0bf891a71c..53b3c6f66b 100644 --- a/cql3/ErrorListener.java +++ b/cql3/error_listener.hh @@ -15,16 +15,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.cassandra.cql3; -import org.antlr.runtime.BaseRecognizer; -import org.antlr.runtime.RecognitionException; +/* + * Copyright 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ + +#pragma once + +#include +#include "core/sstring.hh" + +namespace cql3 { /** * Listener used to collect the syntax errors emitted by the Lexer and Parser. */ -public interface ErrorListener -{ +template +class error_listener { +public: /** * Invoked when a syntax error occurs. * @@ -32,7 +42,7 @@ public interface ErrorListener * @param tokenNames the token names * @param e the exception */ - void syntaxError(BaseRecognizer recognizer, String[] tokenNames, RecognitionException e); + virtual void syntax_error(RecognizerType& recognizer, const std::vector& token_names) = 0; /** * Invoked when a syntax error with a specified message occurs. @@ -40,5 +50,7 @@ public interface ErrorListener * @param recognizer the parser or lexer that emitted the error * @param errorMsg the error message */ - void syntaxError(BaseRecognizer recognizer, String errorMsg); + virtual void syntax_error(RecognizerType& recognizer, const sstring& error_msg) = 0; +}; + } From 5e742cc13d240dafabea8022a9cb3a5a17a4d7cc Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Mon, 9 Feb 2015 21:12:31 +0100 Subject: [PATCH 37/52] cql3: Convert QueryProcessor --- configure.py | 1 + cql3/Cql.g | 48 +++--- cql3/query_processor.cc | 125 ++++++++++++++ ...QueryProcessor.java => query_processor.hh} | 158 +++++------------- exceptions/exceptions.hh | 5 + transport/messages/result_message.hh | 8 +- unimplemented.hh | 3 + 7 files changed, 202 insertions(+), 146 deletions(-) create mode 100644 cql3/query_processor.cc rename cql3/{QueryProcessor.java => query_processor.hh} (81%) diff --git a/configure.py b/configure.py index 50660a29e2..b7a16d93f2 100755 --- a/configure.py +++ b/configure.py @@ -249,6 +249,7 @@ urchin_core = (['database.cc', 'cql3/relation.cc', 'cql3/column_identifier.cc', 'cql3/constants.cc', + 'cql3/query_processor.cc', 'cql3/query_options.cc', 'db/db.cc', 'io/io.cc', diff --git a/cql3/Cql.g b/cql3/Cql.g index f17eebfe01..16664559de 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -25,12 +25,17 @@ options { @parser::namespace{cql3_parser} +@lexer::includes { +#include "cql3/error_listener.hh" +} + @parser::includes { #include "cql3/statements/select_statement.hh" #include "cql3/statements/update_statement.hh" #include "cql3/statements/use_statement.hh" #include "cql3/selection/raw_selector.hh" #include "cql3/constants.hh" +#include "cql3/error_listener.hh" #include "cql3/cql3_type.hh" #include "cql3/cf_name.hh" #include "core/sstring.hh" @@ -77,11 +82,10 @@ using cql3::native_cql3_type; } @context { -#if 0 - private final List listeners = new ArrayList(); -#endif + using listener_type = cql3::error_listener; + listener_type* listener; -std::vector<::shared_ptr> _bind_variables; + std::vector<::shared_ptr> _bind_variables; #if 0 public static final Set reservedTypeNames = new HashSet() @@ -123,29 +127,25 @@ std::vector<::shared_ptr> _bind_variables; bindVariables.add(name); return marker; } +#endif - public void addErrorListener(ErrorListener listener) - { - this.listeners.add(listener); - } - - public void removeErrorListener(ErrorListener listener) - { - this.listeners.remove(listener); + void set_error_listener(listener_type& listener) { + this->listener = &listener; } +#if 0 public void displayRecognitionError(String[] tokenNames, RecognitionException e) { for (int i = 0, m = listeners.size(); i < m; i++) listeners.get(i).syntaxError(this, tokenNames, e); } +#endif - private void addRecognitionError(String msg) - { - for (int i = 0, m = listeners.size(); i < m; i++) - listeners.get(i).syntaxError(this, msg); + void add_recognition_error(const sstring& msg) { + listener->syntax_error(*this, msg); } +#if 0 public Map convertPropertyMap(Maps.Literal map) { if (map == null || map.entries == null || map.entries.isEmpty()) @@ -232,19 +232,17 @@ std::vector<::shared_ptr> _bind_variables; return new CommonToken(Token.EOF); return tokens.remove(0); } +#endif - private final List listeners = new ArrayList(); + using listener_type = cql3::error_listener; - public void addErrorListener(ErrorListener listener) - { - this.listeners.add(listener); - } - - public void removeErrorListener(ErrorListener listener) - { - this.listeners.remove(listener); + listener_type* listener; + + void set_error_listener(listener_type& listener) { + this->listener = &listener; } +#if 0 public void displayRecognitionError(String[] tokenNames, RecognitionException e) { for (int i = 0, m = listeners.size(); i < m; i++) diff --git a/cql3/query_processor.cc b/cql3/query_processor.cc new file mode 100644 index 0000000000..f2592ced6e --- /dev/null +++ b/cql3/query_processor.cc @@ -0,0 +1,125 @@ +/* + * 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 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ + +#include "cql3/query_processor.hh" +#include "cql3/CqlParser.hpp" +#include "cql3/error_collector.hh" + +namespace cql3 { + +using namespace statements; +using namespace transport::messages; + +thread_local logging::logger log("query_processor"); + +future<::shared_ptr> +query_processor::process(const sstring_view& query_string, service::query_state& query_state, query_options& options) +{ + std::unique_ptr p = get_statement(query_string, query_state.get_client_state()); + options.prepare(p->bound_names); + auto cql_statement = p->statement; + if (cql_statement->get_bound_terms() != options.get_values().size()) { + throw exceptions::invalid_request_exception("Invalid amount of bind variables"); + } + + unimplemented::metrics(); +#if 0 + if (!queryState.getClientState().isInternal) + metrics.regularStatementsExecuted.inc(); +#endif + return process_statement(std::move(cql_statement), query_state, options); +} + +future<::shared_ptr> +query_processor::process_statement(::shared_ptr statement, service::query_state& query_state, + const query_options& options) +{ +#if 0 + logger.trace("Process {} @CL.{}", statement, options.getConsistency()); +#endif + auto& client_state = query_state.get_client_state(); + statement->check_access(client_state); + statement->validate(client_state); + + return statement->execute(_proxy, query_state, options) + .then([] (auto msg) { + if (msg) { + return make_ready_future<::shared_ptr>(std::move(msg)); + } + return make_ready_future<::shared_ptr>( + ::make_shared()); + }); +} + +std::unique_ptr +query_processor::get_statement(const sstring_view& query, service::client_state& client_state) +{ +#if 0 + Tracing.trace("Parsing {}", queryStr); +#endif + ::shared_ptr statement = parse_statement(query); + + // Set keyspace for statement that require login + auto cf_stmt = dynamic_pointer_cast(statement); + if (cf_stmt) { + cf_stmt->prepare_keyspace(client_state); + } +#if 0 + Tracing.trace("Preparing statement"); +#endif + return statement->prepare(_db); +} + +::shared_ptr +query_processor::parse_statement(const sstring_view& query) +{ + try { + error_collector lexer_error_collector(query); + error_collector parser_error_collector(query); + cql3_parser::CqlLexer::InputStreamType input{reinterpret_cast(query.begin()), ANTLR_ENC_UTF8, static_cast(query.size()), nullptr}; + cql3_parser::CqlLexer lexer{&input}; + lexer.set_error_listener(lexer_error_collector); + cql3_parser::CqlParser::TokenStreamType tstream(ANTLR_SIZE_HINT, lexer.get_tokSource()); + cql3_parser::CqlParser parser{&tstream}; + parser.set_error_listener(parser_error_collector); + + auto statement = parser.query(); + + lexer_error_collector.throw_first_syntax_error(); + parser_error_collector.throw_first_syntax_error(); + + if (!statement) { + // TODO: We should plug into get_rec()->displayRecognitionError and call error_collector from there + throw exceptions::syntax_exception("Parsing failed"); + }; + return std::move(statement); + } catch (const exceptions::recognition_exception& e) { + throw exceptions::syntax_exception(sprint("Invalid or malformed CQL query string: %s", e.what())); + } catch (const std::exception& e) { + log.error("The statement: {} could not be parsed: {}", query, e.what()); + throw exceptions::syntax_exception(sprint("Failed parsing statement: [%s] reason: %s", query, e.what())); + } +} + +} diff --git a/cql3/QueryProcessor.java b/cql3/query_processor.hh similarity index 81% rename from cql3/QueryProcessor.java rename to cql3/query_processor.hh index ae099724bf..6cd345e9e4 100644 --- a/cql3/QueryProcessor.java +++ b/cql3/query_processor.hh @@ -15,56 +15,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.cassandra.cql3; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +/* + * Copyright 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ -import com.google.common.primitives.Ints; -import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; -import com.googlecode.concurrentlinkedhashmap.EntryWeigher; -import com.googlecode.concurrentlinkedhashmap.EvictionListener; +#include -import org.antlr.runtime.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +#include "core/shared_ptr.hh" +#include "exceptions/exceptions.hh" +#include "cql3/query_options.hh" +#include "cql3/statements/cf_statement.hh" +#include "service/query_state.hh" +#include "log.hh" -import org.apache.cassandra.concurrent.ScheduledExecutors; -import org.apache.cassandra.cql3.functions.*; +namespace cql3 { -import org.apache.cassandra.cql3.statements.*; -import org.apache.cassandra.db.*; -import org.apache.cassandra.db.composites.CType; -import org.apache.cassandra.db.composites.CellName; -import org.apache.cassandra.db.composites.CellNameType; -import org.apache.cassandra.db.composites.Composite; -import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.exceptions.RequestExecutionException; -import org.apache.cassandra.exceptions.RequestValidationException; -import org.apache.cassandra.exceptions.SyntaxException; -import org.apache.cassandra.metrics.CQLMetrics; -import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.service.IMigrationListener; -import org.apache.cassandra.service.MigrationManager; -import org.apache.cassandra.service.QueryState; -import org.apache.cassandra.service.pager.QueryPager; -import org.apache.cassandra.service.pager.QueryPagers; -import org.apache.cassandra.thrift.ThriftClientState; -import org.apache.cassandra.tracing.Tracing; -import org.apache.cassandra.transport.messages.ResultMessage; -import org.apache.cassandra.utils.FBUtilities; -import org.apache.cassandra.utils.MD5Digest; -import org.apache.cassandra.utils.SemanticVersion; -import org.github.jamm.MemoryMeter; +class query_processor { +private: + service::storage_proxy& _proxy; + database& _db; +public: + query_processor(service::storage_proxy& proxy, database& db) : _proxy(proxy), _db(db) {} -public class QueryProcessor implements QueryHandler -{ +#if 0 public static final SemanticVersion CQL_VERSION = new SemanticVersion("3.2.0"); public static final QueryProcessor instance = new QueryProcessor(); @@ -227,40 +203,23 @@ public class QueryProcessor implements QueryHandler serializedSize, Cell.MAX_NAME_LENGTH)); } +#endif +public: + future<::shared_ptr> process_statement(::shared_ptr statement, + service::query_state& query_state, const query_options& options); - public ResultMessage processStatement(CQLStatement statement, QueryState queryState, QueryOptions options) - throws RequestExecutionException, RequestValidationException - { - logger.trace("Process {} @CL.{}", statement, options.getConsistency()); - ClientState clientState = queryState.getClientState(); - statement.checkAccess(clientState); - statement.validate(clientState); - - ResultMessage result = statement.execute(queryState, options); - return result == null ? new ResultMessage.Void() : result; - } - +#if 0 public static ResultMessage process(String queryString, ConsistencyLevel cl, QueryState queryState) throws RequestExecutionException, RequestValidationException { return instance.process(queryString, queryState, QueryOptions.forInternalCalls(cl, Collections.emptyList())); } +#endif - public ResultMessage process(String queryString, QueryState queryState, QueryOptions options) - throws RequestExecutionException, RequestValidationException - { - ParsedStatement.Prepared p = getStatement(queryString, queryState.getClientState()); - options.prepare(p.boundNames); - CQLStatement prepared = p.statement; - if (prepared.getBoundTerms() != options.getValues().size()) - throw new InvalidRequestException("Invalid amount of bind variables"); - - if (!queryState.getClientState().isInternal) - metrics.regularStatementsExecuted.inc(); - - return processStatement(prepared, queryState, options); - } + future<::shared_ptr> process(const std::experimental::string_view& query_string, + service::query_state& query_state, query_options& options); +#if 0 public static ParsedStatement.Prepared parseStatement(String queryStr, QueryState queryState) throws RequestValidationException { return getStatement(queryStr, queryState.getClientState()); @@ -503,58 +462,14 @@ public class QueryProcessor implements QueryHandler batch.validate(clientState); return batch.execute(queryState, options); } +#endif - public static ParsedStatement.Prepared getStatement(String queryStr, ClientState clientState) - throws RequestValidationException - { - Tracing.trace("Parsing {}", queryStr); - ParsedStatement statement = parseStatement(queryStr); - - // Set keyspace for statement that require login - if (statement instanceof CFStatement) - ((CFStatement)statement).prepareKeyspace(clientState); - - Tracing.trace("Preparing statement"); - return statement.prepare(); - } - - public static ParsedStatement parseStatement(String queryStr) throws SyntaxException - { - try - { - // Lexer and parser - ErrorCollector errorCollector = new ErrorCollector(queryStr); - CharStream stream = new ANTLRStringStream(queryStr); - CqlLexer lexer = new CqlLexer(stream); - lexer.addErrorListener(errorCollector); - - TokenStream tokenStream = new CommonTokenStream(lexer); - CqlParser parser = new CqlParser(tokenStream); - parser.addErrorListener(errorCollector); - - // Parse the query string to a statement instance - ParsedStatement statement = parser.query(); - - // The errorCollector has queue up any errors that the lexer and parser may have encountered - // along the way, if necessary, we turn the last error into exceptions here. - errorCollector.throwFirstSyntaxError(); - - return statement; - } - catch (RuntimeException re) - { - logger.error(String.format("The statement: [%s] could not be parsed.", queryStr), re); - throw new SyntaxException(String.format("Failed parsing statement: [%s] reason: %s %s", - queryStr, - re.getClass().getSimpleName(), - re.getMessage())); - } - catch (RecognitionException e) - { - throw new SyntaxException("Invalid or malformed CQL query string: " + e.getMessage()); - } - } +public: + std::unique_ptr get_statement(const std::experimental::string_view& query, + service::client_state& client_state); + static ::shared_ptr parse_statement(const std::experimental::string_view& query); +#if 0 private static long measure(Object key) { return meter.measureDeep(key); @@ -659,4 +574,7 @@ public class QueryProcessor implements QueryHandler iterator.remove(); } } +#endif +}; + } diff --git a/exceptions/exceptions.hh b/exceptions/exceptions.hh index 614dbc195b..10dd429c60 100644 --- a/exceptions/exceptions.hh +++ b/exceptions/exceptions.hh @@ -82,6 +82,11 @@ public: { } }; +class recognition_exception : public std::runtime_error { +public: + recognition_exception(const std::string& msg) : std::runtime_error(msg) {}; +}; + } #endif diff --git a/transport/messages/result_message.hh b/transport/messages/result_message.hh index 8eecb6ddd4..2673028528 100644 --- a/transport/messages/result_message.hh +++ b/transport/messages/result_message.hh @@ -5,8 +5,14 @@ namespace transport { namespace messages { +// FIXME: stub class result_message { - // FIXME: stub + virtual ~result_message() {} + + class void_message; +}; + +class result_message::void_message : public result_message { }; } diff --git a/unimplemented.hh b/unimplemented.hh index 29fe75ebd6..315273b2a0 100644 --- a/unimplemented.hh +++ b/unimplemented.hh @@ -50,4 +50,7 @@ void triggers() { warn("triggers"); } +static inline +void metrics() {} + } From 43300e99981b34045480a74865ec2ad0f4256a66 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:45:31 +0100 Subject: [PATCH 38/52] cql3: Use find() instead of [] when looking up processed keys find() is sufficient and it has less surprising side effects. This doesn't fix any issue. --- cql3/statements/modification_statement.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cql3/statements/modification_statement.cc b/cql3/statements/modification_statement.cc index d920c0f38d..15f1211b20 100644 --- a/cql3/statements/modification_statement.cc +++ b/cql3/statements/modification_statement.cc @@ -141,7 +141,7 @@ modification_statement::read_required_rows( const column_definition* modification_statement::get_first_empty_key() { for (auto& def : s->clustering_key) { - if (!_processed_keys[&def]) { + if (_processed_keys.find(&def) == _processed_keys.end()) { return &def; } } @@ -154,8 +154,8 @@ modification_statement::create_clustering_prefix_internal(const query_options& o const column_definition* first_empty_key = nullptr; for (auto& def : s->clustering_key) { - auto r = _processed_keys[&def]; - if (!r) { + auto i = _processed_keys.find(&def); + if (i == _processed_keys.end()) { first_empty_key = &def; // Tomek: Origin had "&& s->comparator->is_composite()" in the condition below. // Comparator is a thrift concept, not CQL concept, and we want to avoid @@ -172,7 +172,7 @@ modification_statement::create_clustering_prefix_internal(const query_options& o } else if (first_empty_key) { throw exceptions::invalid_request_exception(sprint("Missing PRIMARY KEY part %s since %s is set", first_empty_key->name_as_text(), def.name_as_text())); } else { - auto values = r->values(options); + auto values = i->second->values(options); assert(values.size() == 1); auto val = values[0]; if (!val) { @@ -230,12 +230,12 @@ modification_statement::build_partition_keys(const query_options& options) { auto remaining = s->partition_key.size(); for (auto& def : s->partition_key) { - auto r = _processed_keys[&def]; - if (!r) { + auto i = _processed_keys.find(&def); + if (i == _processed_keys.end()) { throw exceptions::invalid_request_exception(sprint("Missing mandatory PRIMARY KEY part %s", def.name_as_text())); } - auto values = r->values(options); + auto values = i->second->values(options); if (remaining == 1) { if (values.size() == 1) { From 0293b151dc6e2d19025c1549c0558a27f7e69158 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:47:09 +0100 Subject: [PATCH 39/52] cql3: Fix bug in modification_statement::process_where_clause() --- cql3/statements/modification_statement.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cql3/statements/modification_statement.cc b/cql3/statements/modification_statement.cc index 15f1211b20..7497957830 100644 --- a/cql3/statements/modification_statement.cc +++ b/cql3/statements/modification_statement.cc @@ -378,7 +378,7 @@ modification_statement::process_where_clause(std::vector where_cla case column_definition::column_kind::CLUSTERING: if (rel->is_EQ() || (def->is_partition_key() && rel->is_IN())) { add_key_values(*def, rel->to_restriction(s, std::move(names))); - return; + break; } throw exceptions::invalid_request_exception(sprint("Invalid operator %s for PRIMARY KEY part %s", rel->get_operator(), def->name_as_text())); default: From abaf309b6caee59152200d74d9bd53d0acf864eb Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:48:06 +0100 Subject: [PATCH 40/52] cql3: Add operator_type::operator!=() --- cql3/operator.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cql3/operator.hh b/cql3/operator.hh index 7d79763b3e..9f529a7ecb 100644 --- a/cql3/operator.hh +++ b/cql3/operator.hh @@ -55,7 +55,7 @@ public: const operator_type& reverse() const { return _reverse; } sstring to_string() const { return _text; } bool operator==(const operator_type& other) const { return this == &other; } - + bool operator!=(const operator_type& other) const { return this != &other; } #if 0 /** From 3b2c32a9fddf1b6a6bda4c8aa1ed84274d438b4f Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:48:39 +0100 Subject: [PATCH 41/52] cql3: Fix misinitialization of relation::_relation_type --- cql3/relation.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cql3/relation.hh b/cql3/relation.hh index 93b09c4065..83a66c35cf 100644 --- a/cql3/relation.hh +++ b/cql3/relation.hh @@ -38,7 +38,7 @@ protected: const operator_type& _relation_type; public: relation(const operator_type& relation_type) - : _relation_type(_relation_type) { + : _relation_type(relation_type) { } virtual ~relation() {} From bd2892c8fb39593ad74aa8c320ee057cf7f8a47f Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:49:09 +0100 Subject: [PATCH 42/52] cql3: Cleanup code formatting --- cql3/relation.hh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cql3/relation.hh b/cql3/relation.hh index 83a66c35cf..a9c8216ddb 100644 --- a/cql3/relation.hh +++ b/cql3/relation.hh @@ -106,8 +106,7 @@ public: * @return true if the operator of this relation is a Slice, false otherwise. */ virtual bool is_slice() final { - return _relation_type == operator_type:: - GT + return _relation_type == operator_type::GT || _relation_type == operator_type::GTE || _relation_type == operator_type::LTE || _relation_type == From 6cd524988d4d20ef95d0894a1fd574472135798a Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:50:52 +0100 Subject: [PATCH 43/52] db: Add more methods to schema --- database.cc | 6 ++++++ schema.hh | 5 +++++ unimplemented.hh | 3 +++ 3 files changed, 14 insertions(+) diff --git a/database.cc b/database.cc index f9a890ab46..811da2e3bf 100644 --- a/database.cc +++ b/database.cc @@ -4,6 +4,7 @@ #include "log.hh" #include "database.hh" +#include "unimplemented.hh" #include "core/future-util.hh" #include "cql3/column_identifier.hh" @@ -382,3 +383,8 @@ mutation_partition::find_row(const clustering_key& key) { } return &i->second.cells; } + +bool column_definition::is_compact_value() const { + unimplemented::compact_tables(); + return false; +} diff --git a/schema.hh b/schema.hh index 1d949212da..1597af6013 100644 --- a/schema.hh +++ b/schema.hh @@ -26,7 +26,9 @@ public: ::shared_ptr column_specification; bool is_static() const { return kind == column_kind::STATIC; } bool is_partition_key() const { return kind == column_kind::PARTITION; } + bool is_primary_key() const { return kind == column_kind::PARTITION || kind == column_kind::CLUSTERING; } bool is_atomic() const { return !type->is_multi_cell(); } + bool is_compact_value() const; const sstring& name_as_text() const; const bytes& name() const; }; @@ -115,6 +117,9 @@ public: column_definition& regular_column_at(column_id id) { return regular_columns[id]; } + bool is_last_partition_key(column_definition& def) { + return &partition_key[partition_key.size() - 1] == &def; + } }; using schema_ptr = lw_shared_ptr; diff --git a/unimplemented.hh b/unimplemented.hh index 315273b2a0..063ce3d7bc 100644 --- a/unimplemented.hh +++ b/unimplemented.hh @@ -53,4 +53,7 @@ void triggers() { static inline void metrics() {} +static inline +void compact_tables() {} + } From aaf9463568b5b6c4eb760c28e9ae5d7f401dd792 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:51:07 +0100 Subject: [PATCH 44/52] db: Take names by const& in find_*() functions --- database.cc | 6 +++--- database.hh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/database.cc b/database.cc index 811da2e3bf..e788f14d24 100644 --- a/database.cc +++ b/database.cc @@ -230,7 +230,7 @@ column_definition::name() const { } column_family* -keyspace::find_column_family(sstring cf_name) { +keyspace::find_column_family(const sstring& cf_name) { auto i = column_families.find(cf_name); if (i == column_families.end()) { return nullptr; @@ -239,7 +239,7 @@ keyspace::find_column_family(sstring cf_name) { } schema_ptr -keyspace::find_schema(sstring cf_name) { +keyspace::find_schema(const sstring& cf_name) { auto cf = find_column_family(cf_name); if (!cf) { return {}; @@ -248,7 +248,7 @@ keyspace::find_schema(sstring cf_name) { } keyspace* -database::find_keyspace(sstring name) { +database::find_keyspace(const sstring& name) { auto i = keyspaces.find(name); if (i != keyspaces.end()) { return &i->second; diff --git a/database.hh b/database.hh index c7c8fb1e6b..04395a8ccd 100644 --- a/database.hh +++ b/database.hh @@ -217,15 +217,15 @@ class keyspace { public: std::unordered_map column_families; static future populate(sstring datadir); - schema_ptr find_schema(sstring cf_name); - column_family* find_column_family(sstring cf_name); + schema_ptr find_schema(const sstring& cf_name); + column_family* find_column_family(const sstring& cf_name); }; class database { public: std::unordered_map keyspaces; static future populate(sstring datadir); - keyspace* find_keyspace(sstring name); + keyspace* find_keyspace(const sstring& name); }; From ee699bff1ca060df94e5d49ab584d1d2c9ea7d04 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 17:49:06 +0100 Subject: [PATCH 45/52] cql3: Convert more of constants:: --- cql3/constants.cc | 109 ++++++++++++++++++++++++++++++++++++++++++- cql3/constants.hh | 116 ++++------------------------------------------ cql3/cql3_type.cc | 24 +++++----- cql3/cql3_type.hh | 40 ++++++++++++++-- 4 files changed, 165 insertions(+), 124 deletions(-) diff --git a/cql3/constants.cc b/cql3/constants.cc index 9026eaca34..3a7f431c69 100644 --- a/cql3/constants.cc +++ b/cql3/constants.cc @@ -1,8 +1,115 @@ #include "cql3/constants.hh" +#include "cql3/cql3_type.hh" namespace cql3 { const ::shared_ptr constants::NULL_LITERAL = ::make_shared(); const ::shared_ptr constants::null_literal::NULL_VALUE = ::make_shared(); -} \ No newline at end of file +std::ostream& +operator<<(std::ostream&out, constants::type t) +{ + switch (t) { + case constants::type::STRING: return out << "STRING"; + case constants::type::INTEGER: return out << "INTEGER"; + case constants::type::UUID: return out << "UUID"; + case constants::type::FLOAT: return out << "FLOAT"; + case constants::type::BOOLEAN: return out << "BOOLEAN"; + case constants::type::HEX: return out << "HEX"; + }; + assert(0); +} + +bytes +constants::literal::parsed_value(::shared_ptr validator) +{ + try { + if (_type == type::HEX && validator == bytes_type) { + auto v = static_cast(_text); + v.remove_prefix(2); + return validator->from_string(v); + } + if (validator->is_counter()) { + return long_type->from_string(_text); + } + return validator->from_string(_text); + } catch (const exceptions::marshal_exception& e) { + throw exceptions::invalid_request_exception(e.what()); + } +} + +assignment_testable::test_result +constants::literal::test_assignment(const sstring& keyspace, ::shared_ptr receiver) +{ + auto receiver_type = receiver->type->as_cql3_type(); + if (receiver_type->is_collection()) { + return test_result::NOT_ASSIGNABLE; + } + if (!receiver_type->is_native()) { + return test_result::WEAKLY_ASSIGNABLE; + } + auto kind = static_cast(receiver_type.get())->get_kind(); + switch (_type) { + case type::STRING: + if (native_cql3_type::kind_enum_set::frozen< + native_cql3_type::kind::ASCII, + native_cql3_type::kind::TEXT, + native_cql3_type::kind::INET, + native_cql3_type::kind::VARCHAR, + native_cql3_type::kind::TIMESTAMP>::contains(kind)) { + return assignment_testable::test_result::WEAKLY_ASSIGNABLE; + } + break; + case type::INTEGER: + if (native_cql3_type::kind_enum_set::frozen< + native_cql3_type::kind::BIGINT, + native_cql3_type::kind::COUNTER, + native_cql3_type::kind::DECIMAL, + native_cql3_type::kind::DOUBLE, + native_cql3_type::kind::FLOAT, + native_cql3_type::kind::INT, + native_cql3_type::kind::TIMESTAMP, + native_cql3_type::kind::VARINT>::contains(kind)) { + return assignment_testable::test_result::WEAKLY_ASSIGNABLE; + } + break; + case type::UUID: + if (native_cql3_type::kind_enum_set::frozen< + native_cql3_type::kind::UUID, + native_cql3_type::kind::TIMEUUID>::contains(kind)) { + return assignment_testable::test_result::WEAKLY_ASSIGNABLE; + } + break; + case type::FLOAT: + if (native_cql3_type::kind_enum_set::frozen< + native_cql3_type::kind::DECIMAL, + native_cql3_type::kind::DOUBLE, + native_cql3_type::kind::FLOAT>::contains(kind)) { + return assignment_testable::test_result::WEAKLY_ASSIGNABLE; + } + break; + case type::BOOLEAN: + if (kind == native_cql3_type::kind_enum_set::prepare()) { + return assignment_testable::test_result::WEAKLY_ASSIGNABLE; + } + break; + case type::HEX: + if (kind == native_cql3_type::kind_enum_set::prepare()) { + return assignment_testable::test_result::WEAKLY_ASSIGNABLE; + } + break; + } + return assignment_testable::test_result::NOT_ASSIGNABLE; +} + +::shared_ptr +constants::literal::prepare(const sstring& keyspace, ::shared_ptr receiver) +{ + if (!is_assignable(test_assignment(keyspace, receiver))) { + 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(std::experimental::make_optional(parsed_value(receiver->type))); +} + +} diff --git a/cql3/constants.hh b/cql3/constants.hh index 29a4139dcb..5f45a0ed29 100644 --- a/cql3/constants.hh +++ b/cql3/constants.hh @@ -145,115 +145,15 @@ public: return ::make_shared(type::HEX, text); } - virtual ::shared_ptr prepare(const sstring& keyspace, ::shared_ptr receiver) override { - throw std::runtime_error("not implemented"); -#if 0 - if (!testAssignment(keyspace, receiver).isAssignable()) - throw new InvalidRequestException(String.format("Invalid %s constant (%s) for \"%s\" of type %s", type, text, receiver.name, receiver.type.asCQL3Type())); - - return new Value(parsedValue(receiver.type)); -#endif - } -#if 0 - private ByteBuffer parsedValue(AbstractType validator) throws InvalidRequestException - { - if (validator instanceof ReversedType) - validator = ((ReversedType) validator).baseType; - try - { - // BytesType doesn't want it's input prefixed by '0x'. - if (type == Type.HEX && validator instanceof BytesType) - return validator.fromString(text.substring(2)); - if (validator instanceof CounterColumnType) - return LongType.instance.fromString(text); - return validator.fromString(text); - } - catch (MarshalException e) - { - throw new InvalidRequestException(e.getMessage()); - } + virtual ::shared_ptr prepare(const sstring& keyspace, ::shared_ptr receiver); + private: + bytes parsed_value(::shared_ptr validator); + public: + const sstring& get_raw_text() { + return _text; } - public String getRawText() - { - return text; - } -#endif - - virtual assignment_testable::test_result test_assignment(const sstring& keyspace, ::shared_ptr receiver) override { - throw new std::runtime_error("not implemented"); -#if 0 - CQL3Type receiverType = receiver.type.asCQL3Type(); - if (receiverType.isCollection()) - return AssignmentTestable.TestResult.NOT_ASSIGNABLE; - - if (!(receiverType instanceof CQL3Type.Native)) - // Skip type validation for custom types. May or may not be a good idea - return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; - - CQL3Type.Native nt = (CQL3Type.Native)receiverType; - switch (type) - { - case STRING: - switch (nt) - { - case ASCII: - case TEXT: - case INET: - case VARCHAR: - case TIMESTAMP: - return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; - } - break; - case INTEGER: - switch (nt) - { - case BIGINT: - case COUNTER: - case DECIMAL: - case DOUBLE: - case FLOAT: - case INT: - case TIMESTAMP: - case VARINT: - return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; - } - break; - case UUID: - switch (nt) - { - case UUID: - case TIMEUUID: - return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; - } - break; - case FLOAT: - switch (nt) - { - case DECIMAL: - case DOUBLE: - case FLOAT: - return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; - } - break; - case BOOLEAN: - switch (nt) - { - case BOOLEAN: - return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; - } - break; - case HEX: - switch (nt) - { - case BLOB: - return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; - } - break; - } - return AssignmentTestable.TestResult.NOT_ASSIGNABLE; -#endif - } + virtual assignment_testable::test_result test_assignment(const sstring& keyspace, ::shared_ptr receiver); virtual sstring to_string() override { return _type == type::STRING ? sstring(sprint("'%s'", _text)) : _text; @@ -380,6 +280,8 @@ public: #endif }; +std::ostream& operator<<(std::ostream&out, constants::type t); + } #endif diff --git a/cql3/cql3_type.cc b/cql3/cql3_type.cc index 2879994a01..7980bd098f 100644 --- a/cql3/cql3_type.cc +++ b/cql3/cql3_type.cc @@ -6,18 +6,18 @@ namespace cql3 { -thread_local shared_ptr native_cql3_type::ascii = make("ascii", ascii_type); -thread_local shared_ptr native_cql3_type::bigint = make("bigint", long_type); -thread_local shared_ptr native_cql3_type::blob = make("blob", bytes_type); -thread_local shared_ptr native_cql3_type::boolean = make("boolean", boolean_type); -//thread_local shared_ptr native_cql3_type::double = make("double", double_type); -//thread_local shared_ptr native_cql3_type::float = make("float", float_type); -thread_local shared_ptr native_cql3_type::int_ = make("int", int32_type); -thread_local shared_ptr native_cql3_type::text = make("text", utf8_type); -thread_local shared_ptr native_cql3_type::timestamp = make("timestamp", timestamp_type); -thread_local shared_ptr native_cql3_type::uuid = make("uuid", uuid_type); -thread_local shared_ptr native_cql3_type::varchar = make("varchar", utf8_type); -thread_local shared_ptr native_cql3_type::timeuuid = make("timeuuid", timeuuid_type); +thread_local shared_ptr native_cql3_type::ascii = make("ascii", ascii_type, native_cql3_type::kind::ASCII); +thread_local shared_ptr native_cql3_type::bigint = make("bigint", long_type, native_cql3_type::kind::BIGINT); +thread_local shared_ptr native_cql3_type::blob = make("blob", bytes_type, native_cql3_type::kind::BLOB); +thread_local shared_ptr native_cql3_type::boolean = make("boolean", boolean_type, native_cql3_type::kind::BOOLEAN); +//thread_local shared_ptr native_cql3_type::double = make("double", double_type, native_cql3_type::kind::DOUBLE); +//thread_local shared_ptr native_cql3_type::float = make("float", float_type, native_cql3_type::kind::FLOAT); +thread_local shared_ptr native_cql3_type::int_ = make("int", int32_type, native_cql3_type::kind::INT); +thread_local shared_ptr native_cql3_type::text = make("text", utf8_type, native_cql3_type::kind::TEXT); +thread_local shared_ptr native_cql3_type::timestamp = make("timestamp", timestamp_type, native_cql3_type::kind::TIMESTAMP); +thread_local shared_ptr native_cql3_type::uuid = make("uuid", uuid_type, native_cql3_type::kind::UUID); +thread_local shared_ptr native_cql3_type::varchar = make("varchar", utf8_type, native_cql3_type::kind::TEXT); +thread_local shared_ptr native_cql3_type::timeuuid = make("timeuuid", timeuuid_type, native_cql3_type::kind::TIMEUUID); const std::vector>& native_cql3_type::values() { diff --git a/cql3/cql3_type.hh b/cql3/cql3_type.hh index 78a300b1b0..b573add21c 100644 --- a/cql3/cql3_type.hh +++ b/cql3/cql3_type.hh @@ -24,9 +24,10 @@ #pragma once -#include "database.hh" +#include "types.hh" #include "exceptions/exceptions.hh" #include +#include "enum_set.hh" namespace cql3 { @@ -37,13 +38,38 @@ public: cql3_type(sstring name, data_type type) : _name(std::move(name)), _type(std::move(type)) {} virtual ~cql3_type() {} virtual bool is_collection() const = 0; + virtual bool is_native() const { return false; } data_type get_type() const { return _type; } sstring to_string() const { return _name; } }; class native_cql3_type : public cql3_type { - static shared_ptr make(sstring name, data_type type) { - return make_shared(std::move(name), std::move(type)); +public: + enum class kind : int8_t { + ASCII, BIGINT, BLOB, BOOLEAN, COUNTER, DECIMAL, DOUBLE, FLOAT, INT, INET, TEXT, TIMESTAMP, UUID, VARCHAR, VARINT, TIMEUUID + }; + using kind_enum = super_enum; + using kind_enum_set = enum_set; +private: + kind_enum_set::prepared _kind; + static shared_ptr make(sstring name, data_type type, kind kind_) { + return make_shared(std::move(name), std::move(type), kind_); } public: static thread_local shared_ptr ascii; @@ -67,10 +93,16 @@ public: #endif static const std::vector>& values(); public: - using cql3_type::cql3_type; + native_cql3_type(sstring name, data_type type, kind kind_) + : cql3_type(std::move(name), std::move(type)), _kind(kind_enum_set::prepare(kind_)) + { } virtual bool is_collection() const override { return false; } + virtual bool is_native() const override { + return true; + } + kind_enum_set::prepared get_kind() const { return _kind; } }; #if 0 From 6ca084fdb37b95fc6da979ef741ee913054987f7 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 18:12:15 +0100 Subject: [PATCH 46/52] cql3: Implement operator== for column_identifier::raw --- cql3/column_identifier.hh | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/cql3/column_identifier.hh b/cql3/column_identifier.hh index 6b976afb43..4330232350 100644 --- a/cql3/column_identifier.hh +++ b/cql3/column_identifier.hh @@ -162,16 +162,9 @@ public: return false; } -#if 0 - @Override - public final boolean equals(Object o) - { - if(!(o instanceof ColumnIdentifier.Raw)) - return false; - ColumnIdentifier.Raw that = (ColumnIdentifier.Raw)o; - return text.equals(that.text); + bool operator==(const raw& other) const { + return _text == other._text; } -#endif virtual sstring to_string() const { return _text; From 08eed72021c6f86faf175bf4a3df217bb4a1b7a4 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 18:15:11 +0100 Subject: [PATCH 47/52] cql3: Convert more of SingleColumnRelation --- configure.py | 1 + cql3/single_column_relation.cc | 149 +++++++++++++++++++++++++++++++++ cql3/single_column_relation.hh | 135 ++++------------------------- unimplemented.hh | 8 ++ 4 files changed, 174 insertions(+), 119 deletions(-) create mode 100644 cql3/single_column_relation.cc diff --git a/configure.py b/configure.py index b7a16d93f2..0066e88e86 100755 --- a/configure.py +++ b/configure.py @@ -251,6 +251,7 @@ urchin_core = (['database.cc', 'cql3/constants.cc', 'cql3/query_processor.cc', 'cql3/query_options.cc', + 'cql3/single_column_relation.cc', 'db/db.cc', 'io/io.cc', 'utils/utils.cc', diff --git a/cql3/single_column_relation.cc b/cql3/single_column_relation.cc new file mode 100644 index 0000000000..308e61e23c --- /dev/null +++ b/cql3/single_column_relation.cc @@ -0,0 +1,149 @@ +/* + * 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 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ + +#include "cql3/single_column_relation.hh" +#include "cql3/restrictions/single_column_restriction.hh" +#include "unimplemented.hh" + +using namespace cql3::restrictions; + +namespace cql3 { + +::shared_ptr +single_column_relation::to_term(std::vector<::shared_ptr> receivers, + ::shared_ptr raw, + const sstring& keyspace, + ::shared_ptr bound_names) +{ + // TODO: optimize vector away, accept single column_specification + assert(receivers.size() == 1); + auto term = raw->prepare(keyspace, receivers[0]); + term->collect_marker_specification(bound_names); + return term; +} + +::shared_ptr +single_column_relation::new_EQ_restriction(schema_ptr schema, ::shared_ptr bound_names) +{ + column_definition& column_def = to_column_definition(schema, _entity); + if (!_map_key) { + auto term = to_term(to_receivers(schema, column_def), _value, schema->ks_name, bound_names); + return ::make_shared(column_def, std::move(term)); + } + unimplemented::collections(); +#if 0 + List receivers = toReceivers(schema, columnDef); + Term entryKey = toTerm(Collections.singletonList(receivers.get(0)), map_key, schema.ksName, bound_names); + Term entryValue = toTerm(Collections.singletonList(receivers.get(1)), value, schema.ksName, bound_names); + return new SingleColumnRestriction.Contains(columnDef, entryKey, entryValue); +#endif +} + +std::vector<::shared_ptr> +single_column_relation::to_receivers(schema_ptr schema, column_definition& column_def) +{ + auto receiver = column_def.column_specification; + + if (column_def.is_compact_value()) { + throw exceptions::invalid_request_exception(sprint( + "Predicates on the non-primary-key column (%s) of a COMPACT table are not yet supported", column_def.name_as_text())); + } + + if (is_IN()) { + // For partition keys we only support IN for the last name so far + if (column_def.is_partition_key() && !schema->is_last_partition_key(column_def)) { + throw exceptions::invalid_request_exception(sprint( + "Partition KEY part %s cannot be restricted by IN relation (only the last part of the partition key can)", + column_def.name_as_text())); + } + + // We only allow IN on the row key and the clustering key so far, never on non-PK columns, and this even if + // there's an index + // Note: for backward compatibility reason, we conside a IN of 1 value the same as a EQ, so we let that + // slide. + if (!column_def.is_primary_key() && !can_have_only_one_value()) { + throw exceptions::invalid_request_exception(sprint( + "IN predicates on non-primary-key columns (%s) is not yet supported", column_def.name_as_text())); + } + } else if (is_slice()) { + // Non EQ relation is not supported without token(), even if we have a 2ndary index (since even those + // are ordered by partitioner). + // Note: In theory we could allow it for 2ndary index queries with ALLOW FILTERING, but that would + // probably require some special casing + // Note bis: This is also why we don't bother handling the 'tuple' notation of #4851 for keys. If we + // lift the limitation for 2ndary + // index with filtering, we'll need to handle it though. + if (column_def.is_partition_key()) { + throw exceptions::invalid_request_exception( + "Only EQ and IN relation are supported on the partition key (unless you use the token() function)"); + } + } + + if (is_contains_key()) { + unimplemented::collections(); +#if 0 + if (!(receiver.type instanceof MapType)) { + throw exceptions::invalid_request_exception(sprint("Cannot use CONTAINS KEY on non-map column %s", receiver.name_as_text())); + } +#endif + } + + if (_map_key) { + unimplemented::collections(); +#if 0 + checkFalse(receiver.type instanceof ListType, "Indexes on list entries (%s[index] = value) are not currently supported.", receiver.name); + checkTrue(receiver.type instanceof MapType, "Column %s cannot be used as a map", receiver.name); + checkTrue(receiver.type.isMultiCell(), "Map-entry equality predicates on frozen map column %s are not supported", receiver.name); + checkTrue(isEQ(), "Only EQ relations are supported on map entries"); +#endif + } + + if (receiver->type->is_collection()) { + unimplemented::collections(); +#if 0 + // We don't support relations against entire collections (unless they're frozen), like "numbers = {1, 2, 3}" + checkFalse(receiver.type.isMultiCell() && !isLegalRelationForNonFrozenCollection(), + "Collection column '%s' (%s) cannot be restricted by a '%s' relation", + receiver.name, + receiver.type.asCQL3Type(), + get_operator()); + + if (isContainsKey() || isContains()) + { + receiver = makeCollectionReceiver(receiver, isContainsKey()); + } + else if (receiver.type.isMultiCell() && map_key != null && isEQ()) + { + List receivers = new ArrayList<>(2); + receivers.add(makeCollectionReceiver(receiver, true)); + receivers.add(makeCollectionReceiver(receiver, false)); + return receivers; + } +#endif + } + + return {std::move(receiver)}; +} + +} diff --git a/cql3/single_column_relation.hh b/cql3/single_column_relation.hh index 8272fc6f02..6431f880f8 100644 --- a/cql3/single_column_relation.hh +++ b/cql3/single_column_relation.hh @@ -77,7 +77,7 @@ public: * @param value the value being compared. */ single_column_relation(::shared_ptr entity, const operator_type& type, ::shared_ptr value) - : single_column_relation(std::move(entity), {}, std::move(type), std::move(value)) + : single_column_relation(std::move(entity), {}, type, std::move(value)) { } #if 0 @@ -95,22 +95,12 @@ public: ::shared_ptr get_map_key() { return _map_key; } +protected: + ::shared_ptr to_term(std::vector<::shared_ptr> receivers, + ::shared_ptr raw, const sstring& keyspace, + ::shared_ptr bound_names); #if 0 - @Override - protected Term toTerm(List receivers, - Raw raw, - String keyspace, - ::shared_ptr boundNames) - throws InvalidRequestException - { - assert receivers.size() == 1; - - Term term = raw.prepare(keyspace, receivers.get(0)); - term.collectMarkerSpecification(boundNames); - return term; - } - public SingleColumnRelation withNonStrictOperator() { switch (relationType) @@ -137,21 +127,7 @@ public: protected: virtual ::shared_ptr new_EQ_restriction(schema_ptr schema, - ::shared_ptr bound_names) override { - throw std::runtime_error("not implemented"); -#if 0 - ColumnDefinition columnDef = toColumnDefinition(schema, entity); - if (map_key == null) - { - Term term = toTerm(toReceivers(schema, columnDef), value, schema.ksName, bound_names); - return new SingleColumnRestriction.EQ(columnDef, term); - } - List receivers = toReceivers(schema, columnDef); - Term entryKey = toTerm(Collections.singletonList(receivers.get(0)), map_key, schema.ksName, bound_names); - Term entryValue = toTerm(Collections.singletonList(receivers.get(1)), value, schema.ksName, bound_names); - return new SingleColumnRestriction.Contains(columnDef, entryKey, entryValue); -#endif - } + ::shared_ptr bound_names); virtual ::shared_ptr new_IN_restriction(schema_ptr schema, ::shared_ptr bound_names) override { @@ -192,84 +168,18 @@ protected: #endif } -#if 0 +private: /** * Returns the receivers for this relation. * * @param schema the Column Family meta data - * @param columnDef the column definition + * @param column_def the column definition * @return the receivers for the specified relation. - * @throws InvalidRequestException if the relation is invalid + * @throws exceptions::invalid_request_exception if the relation is invalid */ - private List toReceivers(schema_ptr schema, ColumnDefinition columnDef) throws InvalidRequestException - { - ColumnSpecification receiver = columnDef; - - checkFalse(columnDef.isCompactValue(), - "Predicates on the non-primary-key column (%s) of a COMPACT table are not yet supported", - columnDef.name); - - if (isIN()) - { - // For partition keys we only support IN for the last name so far - checkFalse(columnDef.isPartitionKey() && !isLastPartitionKey(schema, columnDef), - "Partition KEY part %s cannot be restricted by IN relation (only the last part of the partition key can)", - columnDef.name); - - // We only allow IN on the row key and the clustering key so far, never on non-PK columns, and this even if - // there's an index - // Note: for backward compatibility reason, we conside a IN of 1 value the same as a EQ, so we let that - // slide. - checkFalse(!columnDef.isPrimaryKeyColumn() && !canHaveOnlyOneValue(), - "IN predicates on non-primary-key columns (%s) is not yet supported", columnDef.name); - } - else if (isSlice()) - { - // Non EQ relation is not supported without token(), even if we have a 2ndary index (since even those - // are ordered by partitioner). - // Note: In theory we could allow it for 2ndary index queries with ALLOW FILTERING, but that would - // probably require some special casing - // Note bis: This is also why we don't bother handling the 'tuple' notation of #4851 for keys. If we - // lift the limitation for 2ndary - // index with filtering, we'll need to handle it though. - checkFalse(columnDef.isPartitionKey(), "Only EQ and IN relation are supported on the partition key (unless you use the token() function)"); - } - - checkFalse(isContainsKey() && !(receiver.type instanceof MapType), "Cannot use CONTAINS KEY on non-map column %s", receiver.name); - - if (map_key != null) - { - checkFalse(receiver.type instanceof ListType, "Indexes on list entries (%s[index] = value) are not currently supported.", receiver.name); - checkTrue(receiver.type instanceof MapType, "Column %s cannot be used as a map", receiver.name); - checkTrue(receiver.type.isMultiCell(), "Map-entry equality predicates on frozen map column %s are not supported", receiver.name); - checkTrue(isEQ(), "Only EQ relations are supported on map entries"); - } - - if (receiver.type.isCollection()) - { - // We don't support relations against entire collections (unless they're frozen), like "numbers = {1, 2, 3}" - checkFalse(receiver.type.isMultiCell() && !isLegalRelationForNonFrozenCollection(), - "Collection column '%s' (%s) cannot be restricted by a '%s' relation", - receiver.name, - receiver.type.asCQL3Type(), - get_operator()); - - if (isContainsKey() || isContains()) - { - receiver = makeCollectionReceiver(receiver, isContainsKey()); - } - else if (receiver.type.isMultiCell() && map_key != null && isEQ()) - { - List receivers = new ArrayList<>(2); - receivers.add(makeCollectionReceiver(receiver, true)); - receivers.add(makeCollectionReceiver(receiver, false)); - return receivers; - } - } - - return Collections.singletonList(receiver); - } + std::vector<::shared_ptr> to_receivers(schema_ptr schema, column_definition& column_def); +#if 0 private ColumnSpecification makeCollectionReceiver(ColumnSpecification receiver, bool forKey) { return ((CollectionType) receiver.type).makeCollectionReceiver(receiver, forKey); @@ -284,25 +194,12 @@ protected: { return map_key != null && isEQ(); } - - /** - * Checks if the specified column is the last column of the partition key. - * - * @param schema the column family meta data - * @param columnDef the column to check - * @return true if the specified column is the last column of the partition key, false - * otherwise. - */ - private static bool isLastPartitionKey(schema_ptr schema, ColumnDefinition columnDef) - { - return columnDef.position() == schema.partitionKeyColumns().size() - 1; - } - - private bool canHaveOnlyOneValue() - { - return isEQ() || (isIN() && in_values != null && in_values.size() == 1); - } #endif + +private: + bool can_have_only_one_value() { + return is_EQ() || (is_IN() && _in_values.size() == 1); + } }; }; diff --git a/unimplemented.hh b/unimplemented.hh index 063ce3d7bc..6a64dcdcc2 100644 --- a/unimplemented.hh +++ b/unimplemented.hh @@ -50,6 +50,14 @@ void triggers() { warn("triggers"); } +static inline +void collections() __attribute__((noreturn)); + +static inline +void collections() { + fail("collections"); +} + static inline void metrics() {} From 572b61a2bb9ec7a86a080137c976de9d2537e1ec Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 18:20:13 +0100 Subject: [PATCH 48/52] cql3: Convert more of ColumnCondition and ColumnCondition::Raw --- configure.py | 1 + cql3/abstract_marker.hh | 1 + cql3/column_condition.cc | 120 +++++++++++++++++++ cql3/column_condition.hh | 248 +++++++++++++-------------------------- 4 files changed, 206 insertions(+), 164 deletions(-) create mode 100644 cql3/column_condition.cc diff --git a/configure.py b/configure.py index 0066e88e86..eb9a19cb9a 100755 --- a/configure.py +++ b/configure.py @@ -252,6 +252,7 @@ urchin_core = (['database.cc', 'cql3/query_processor.cc', 'cql3/query_options.cc', 'cql3/single_column_relation.cc', + 'cql3/column_condition.cc', 'db/db.cc', 'io/io.cc', 'utils/utils.cc', diff --git a/cql3/abstract_marker.hh b/cql3/abstract_marker.hh index 6b0da7e195..8fe8362d44 100644 --- a/cql3/abstract_marker.hh +++ b/cql3/abstract_marker.hh @@ -97,6 +97,7 @@ public: return ::make_shared(receiver->ks_name, receiver->cf_name, in_name, db::marshal::list_type::get_instance(receiver->type, false)); } + public: virtual ::shared_ptr prepare(const sstring& keyspace, ::shared_ptr receiver) override; }; }; diff --git a/cql3/column_condition.cc b/cql3/column_condition.cc new file mode 100644 index 0000000000..040c1a4494 --- /dev/null +++ b/cql3/column_condition.cc @@ -0,0 +1,120 @@ +/* + * 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 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ + +#include "cql3/column_condition.hh" +#include "unimplemented.hh" + +namespace cql3 { + +bool +column_condition::uses_function(const sstring& ks_name, const sstring& function_name) { + if (bool(_collection_element) && _collection_element->uses_function(ks_name, function_name)) { + return true; + } + if (bool(_value) && _value->uses_function(ks_name, function_name)) { + return true; + } + if (!_in_values.empty()) { + for (auto&& value : _in_values) { + if (bool(value) && value->uses_function(ks_name, function_name)) { + return true; + } + } + } + return false; +} + +void column_condition::collect_marker_specificaton(::shared_ptr bound_names) { + if (_collection_element) { + _collection_element->collect_marker_specification(bound_names); + } + if (!_in_values.empty()) { + for (auto&& value : _in_values) { + value->collect_marker_specification(bound_names); + } + } + _value->collect_marker_specification(bound_names); +} + +::shared_ptr +column_condition::raw::prepare(const sstring& keyspace, column_definition& receiver) { + if (receiver.type->is_counter()) { + throw exceptions::invalid_request_exception("Conditions on counters are not supported"); + } + + if (!_collection_element) { + if (_op == operator_type::IN) { + if (_in_values.empty()) { // ? + return column_condition::in_condition(receiver, _in_marker->prepare(keyspace, receiver.column_specification)); + } + + std::vector<::shared_ptr> terms; + for (auto&& value : _in_values) { + terms.push_back(value->prepare(keyspace, receiver.column_specification)); + } + return column_condition::in_condition(receiver, std::move(terms)); + } else { + return column_condition::condition(receiver, _value->prepare(keyspace, receiver.column_specification), _op); + } + } + + if (!receiver.type->is_collection()) { + throw exceptions::invalid_request_exception(sprint("Invalid element access syntax for non-collection column %s", receiver.name_as_text())); + } + + unimplemented::collections(); +#if 0 + ColumnSpecification elementSpec, valueSpec; + switch ((((CollectionType)receiver.type).kind)) + { + case LIST: + elementSpec = Lists.indexSpecOf(receiver); + valueSpec = Lists.valueSpecOf(receiver); + break; + case MAP: + elementSpec = Maps.keySpecOf(receiver); + valueSpec = Maps.valueSpecOf(receiver); + break; + case SET: + throw new InvalidRequestException(String.format("Invalid element access syntax for set column %s", receiver.name)); + default: + throw new AssertionError(); + } + if (operator == Operator.IN) + { + if (inValues == null) + return ColumnCondition.inCondition(receiver, collectionElement.prepare(keyspace, elementSpec), inMarker.prepare(keyspace, valueSpec)); + List terms = new ArrayList<>(inValues.size()); + for (Term.Raw value : inValues) + terms.add(value.prepare(keyspace, valueSpec)); + return ColumnCondition.inCondition(receiver, collectionElement.prepare(keyspace, elementSpec), terms); + } + else + { + return ColumnCondition.condition(receiver, collectionElement.prepare(keyspace, elementSpec), value.prepare(keyspace, valueSpec), operator); + } +#endif +} + +} diff --git a/cql3/column_condition.hh b/cql3/column_condition.hh index 13cb41a2ef..edad9c4147 100644 --- a/cql3/column_condition.hh +++ b/cql3/column_condition.hh @@ -25,6 +25,10 @@ #ifndef CQL3_COLUMN_CONDITION_HH #define CQL3_COLUMN_CONDITION_HH +#include "cql3/term.hh" +#include "cql3/abstract_marker.hh" +#include "cql3/operator.hh" + #if 0 import java.nio.ByteBuffer; import java.util.*; @@ -53,74 +57,59 @@ namespace cql3 { class column_condition final { public: column_definition& column; - -#if 0 - // For collection, when testing the equality of a specific element, null otherwise. - private final Term collectionElement; - - private final Term value; // a single value or a marker for a list of IN values - private final List inValues; - - public final Operator operator; - - private ColumnCondition(ColumnDefinition column, Term collectionElement, Term value, List inValues, Operator op) +private: + // For collection, when testing the equality of a specific element, nullptr otherwise. + ::shared_ptr _collection_element; + ::shared_ptr _value; + std::vector<::shared_ptr> _in_values; + const operator_type& _op; +public: + column_condition(column_definition& column, ::shared_ptr collection_element, + ::shared_ptr value, std::vector<::shared_ptr> in_values, const operator_type& op) + : column(column) + , _collection_element(std::move(collection_element)) + , _value(std::move(value)) + , _in_values(std::move(in_values)) + , _op(op) { - this.column = column; - this.collectionElement = collectionElement; - this.value = value; - this.inValues = inValues; - this.operator = op; - - if (operator != Operator.IN) - assert this.inValues == null; + if (op != operator_type::IN) { + assert(_in_values.empty()); + } } - public static ColumnCondition condition(ColumnDefinition column, Term value, Operator op) - { - return new ColumnCondition(column, null, value, null, op); + static ::shared_ptr condition(column_definition& def, ::shared_ptr value, const operator_type& op) { + return ::make_shared(def, ::shared_ptr{}, std::move(value), std::vector<::shared_ptr>{}, op); } - public static ColumnCondition condition(ColumnDefinition column, Term collectionElement, Term value, Operator op) - { - return new ColumnCondition(column, collectionElement, value, null, op); + static ::shared_ptr condition(column_definition& def, ::shared_ptr collection_element, + ::shared_ptr value, const operator_type& op) { + return ::make_shared(def, std::move(collection_element), std::move(value), + std::vector<::shared_ptr>{}, op); } - public static ColumnCondition inCondition(ColumnDefinition column, List inValues) - { - return new ColumnCondition(column, null, null, inValues, Operator.IN); + static ::shared_ptr in_condition(column_definition& def, std::vector<::shared_ptr> in_values) { + return ::make_shared(def, ::shared_ptr{}, ::shared_ptr{}, + std::move(in_values), operator_type::IN); } - public static ColumnCondition inCondition(ColumnDefinition column, Term collectionElement, List inValues) - { - return new ColumnCondition(column, collectionElement, null, inValues, Operator.IN); + static ::shared_ptr in_condition(column_definition& def, ::shared_ptr collection_element, + std::vector<::shared_ptr> in_values) { + return ::make_shared(def, std::move(collection_element), ::shared_ptr{}, + std::move(in_values), operator_type::IN); } - public static ColumnCondition inCondition(ColumnDefinition column, Term inMarker) - { - return new ColumnCondition(column, null, inMarker, null, Operator.IN); + static ::shared_ptr in_condition(column_definition& def, ::shared_ptr in_marker) { + return ::make_shared(def, ::shared_ptr{}, std::move(in_marker), + std::vector<::shared_ptr>{}, operator_type::IN); } - public static ColumnCondition inCondition(ColumnDefinition column, Term collectionElement, Term inMarker) - { - return new ColumnCondition(column, collectionElement, inMarker, null, Operator.IN); - } -#endif - - bool uses_function(const sstring& ks_name, const sstring& function_name) const { - throw std::runtime_error("not implemented"); -#if 0 - if (collectionElement != null && collectionElement.usesFunction(ksName, functionName)) - return true; - if (value != null && value.usesFunction(ksName, functionName)) - return true; - if (inValues != null) - for (Term value : inValues) - if (value != null && value.usesFunction(ksName, functionName)) - return true; - return false; -#endif + static ::shared_ptr in_condition(column_definition& def, ::shared_ptr collection_element, + ::shared_ptr in_marker) { + return ::make_shared(def, std::move(collection_element), std::move(in_marker), + std::vector<::shared_ptr>{}, operator_type::IN); } + bool uses_function(const sstring& ks_name, const sstring& function_name); public: /** * Collects the column specification for the bind variables of this operation. @@ -128,23 +117,7 @@ public: * @param boundNames the list of column specification where to collect the * bind variables of this term in. */ - void collect_marker_specificaton(::shared_ptr bound_names) { - throw std::runtime_error("not found"); -#if 0 - if (collectionElement != null) - collectionElement.collectMarkerSpecification(boundNames); - - if ((operator == Operator.IN) && inValues != null) - { - for (Term value : inValues) - value.collectMarkerSpecification(boundNames); - } - else - { - value.collectMarkerSpecification(boundNames); - } -#endif - } + void collect_marker_specificaton(::shared_ptr bound_names); #if 0 public ColumnCondition.Bound bind(QueryOptions options) throws InvalidRequestException @@ -725,120 +698,67 @@ public: } #endif - class raw { -#if 0 - private final Term.Raw value; - private final List inValues; - private final AbstractMarker.INRaw inMarker; + class raw final { + private: + ::shared_ptr _value; + std::vector<::shared_ptr> _in_values; + ::shared_ptr _in_marker; - // Can be null, only used with the syntax "IF m[e] = ..." (in which case it's 'e') - private final Term.Raw collectionElement; - - private final Operator operator; - - private Raw(Term.Raw value, List inValues, AbstractMarker.INRaw inMarker, Term.Raw collectionElement, Operator op) - { - this.value = value; - this.inValues = inValues; - this.inMarker = inMarker; - this.collectionElement = collectionElement; - this.operator = op; - } + // Can be nullptr, only used with the syntax "IF m[e] = ..." (in which case it's 'e') + ::shared_ptr _collection_element; + const operator_type& _op; + public: + raw(::shared_ptr value, + std::vector<::shared_ptr> in_values, + ::shared_ptr in_marker, + ::shared_ptr collection_element, + const operator_type& op) + : _value(std::move(value)) + , _in_values(std::move(in_values)) + , _in_marker(std::move(in_marker)) + , _collection_element(std::move(collection_element)) + , _op(op) + { } /** A condition on a column. For example: "IF col = 'foo'" */ - public static Raw simpleCondition(Term.Raw value, Operator op) - { - return new Raw(value, null, null, null, op); + static ::shared_ptr simple_condition(::shared_ptr value, const operator_type& op) { + return ::make_shared(std::move(value), std::vector<::shared_ptr>{}, + ::shared_ptr{}, ::shared_ptr{}, op); } /** An IN condition on a column. For example: "IF col IN ('foo', 'bar', ...)" */ - public static Raw simpleInCondition(List inValues) - { - return new Raw(null, inValues, null, null, Operator.IN); + static ::shared_ptr simple_in_condition(std::vector<::shared_ptr> in_values) { + return ::make_shared(::shared_ptr{}, std::move(in_values), + ::shared_ptr{}, ::shared_ptr{}, operator_type::IN); } /** An IN condition on a column with a single marker. For example: "IF col IN ?" */ - public static Raw simpleInCondition(AbstractMarker.INRaw inMarker) - { - return new Raw(null, null, inMarker, null, Operator.IN); + static ::shared_ptr simple_in_condition(::shared_ptr in_marker) { + return ::make_shared(::shared_ptr{}, std::vector<::shared_ptr>{}, + std::move(in_marker), ::shared_ptr{}, operator_type::IN); } /** A condition on a collection element. For example: "IF col['key'] = 'foo'" */ - public static Raw collectionCondition(Term.Raw value, Term.Raw collectionElement, Operator op) - { - return new Raw(value, null, null, collectionElement, op); + static ::shared_ptr collection_condition(::shared_ptr value, ::shared_ptr collection_element, + const operator_type& op) { + return ::make_shared(std::move(value), std::vector<::shared_ptr>{}, ::shared_ptr{}, std::move(collection_element), operator_type::IN); } /** An IN condition on a collection element. For example: "IF col['key'] IN ('foo', 'bar', ...)" */ - public static Raw collectionInCondition(Term.Raw collectionElement, List inValues) - { - return new Raw(null, inValues, null, collectionElement, Operator.IN); + static ::shared_ptr collection_in_condition(::shared_ptr collection_element, + std::vector<::shared_ptr> in_values) { + return ::make_shared(::shared_ptr{}, std::move(in_values), ::shared_ptr{}, + std::move(collection_element), operator_type::IN); } /** An IN condition on a collection element with a single marker. For example: "IF col['key'] IN ?" */ - public static Raw collectionInCondition(Term.Raw collectionElement, AbstractMarker.INRaw inMarker) - { - return new Raw(null, null, inMarker, collectionElement, Operator.IN); + static ::shared_ptr collectionInCondition(::shared_ptr collection_element, + ::shared_ptr in_marker) { + return ::make_shared(::shared_ptr{}, std::vector<::shared_ptr>{}, std::move(in_marker), + std::move(collection_element), operator_type::IN); } -#endif - public: - ::shared_ptr prepare(const sstring& keyspace, column_definition& receiver) { - throw std::runtime_error("not implemented"); -#if 0 - if (receiver.type instanceof CounterColumnType) - throw new InvalidRequestException("Conditions on counters are not supported"); - if (collectionElement == null) - { - if (operator == Operator.IN) - { - if (inValues == null) - return ColumnCondition.inCondition(receiver, inMarker.prepare(keyspace, receiver)); - List terms = new ArrayList<>(inValues.size()); - for (Term.Raw value : inValues) - terms.add(value.prepare(keyspace, receiver)); - return ColumnCondition.inCondition(receiver, terms); - } - else - { - return ColumnCondition.condition(receiver, value.prepare(keyspace, receiver), operator); - } - } - - if (!(receiver.type.isCollection())) - throw new InvalidRequestException(String.format("Invalid element access syntax for non-collection column %s", receiver.name)); - - ColumnSpecification elementSpec, valueSpec; - switch ((((CollectionType)receiver.type).kind)) - { - case LIST: - elementSpec = Lists.indexSpecOf(receiver); - valueSpec = Lists.valueSpecOf(receiver); - break; - case MAP: - elementSpec = Maps.keySpecOf(receiver); - valueSpec = Maps.valueSpecOf(receiver); - break; - case SET: - throw new InvalidRequestException(String.format("Invalid element access syntax for set column %s", receiver.name)); - default: - throw new AssertionError(); - } - if (operator == Operator.IN) - { - if (inValues == null) - return ColumnCondition.inCondition(receiver, collectionElement.prepare(keyspace, elementSpec), inMarker.prepare(keyspace, valueSpec)); - List terms = new ArrayList<>(inValues.size()); - for (Term.Raw value : inValues) - terms.add(value.prepare(keyspace, valueSpec)); - return ColumnCondition.inCondition(receiver, collectionElement.prepare(keyspace, elementSpec), terms); - } - else - { - return ColumnCondition.condition(receiver, collectionElement.prepare(keyspace, elementSpec), value.prepare(keyspace, valueSpec), operator); - } -#endif - } + ::shared_ptr prepare(const sstring& keyspace, column_definition& receiver); }; }; From 87821f2d12e3a58375fec6a99d5ff6072b894501 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 18:25:36 +0100 Subject: [PATCH 49/52] Add missing copyright banners --- cql3/constants.cc | 24 ++++++++++++++++++++++++ exceptions/exceptions.hh | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/cql3/constants.cc b/cql3/constants.cc index 3a7f431c69..834a2a15f4 100644 --- a/cql3/constants.cc +++ b/cql3/constants.cc @@ -1,3 +1,27 @@ +/* + * 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 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ + #include "cql3/constants.hh" #include "cql3/cql3_type.hh" diff --git a/exceptions/exceptions.hh b/exceptions/exceptions.hh index 10dd429c60..74dafa2469 100644 --- a/exceptions/exceptions.hh +++ b/exceptions/exceptions.hh @@ -1,3 +1,27 @@ +/* + * 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 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ + #ifndef EXCEPTIONS_HH #define EXCEPTIONS_HH From 14546bf5c58503e33b4740dba01bcd703b1e9ed6 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 18:26:04 +0100 Subject: [PATCH 50/52] cql3: Convert rules for parsing UPDATE statements --- cql3/Cql.g | 128 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/cql3/Cql.g b/cql3/Cql.g index 16664559de..b0f66c8874 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -35,7 +35,9 @@ options { #include "cql3/statements/use_statement.hh" #include "cql3/selection/raw_selector.hh" #include "cql3/constants.hh" +#include "cql3/operation_impl.hh" #include "cql3/error_listener.hh" +#include "cql3/single_column_relation.hh" #include "cql3/cql3_type.hh" #include "cql3/cf_name.hh" #include "core/sstring.hh" @@ -49,6 +51,8 @@ using namespace cql3::statements; using namespace cql3::selection; using cql3::cql3_type; using cql3::native_cql3_type; +using conditions_type = std::vector,::shared_ptr>>; +using operations_type = std::vector,::shared_ptr>>; } @header { @@ -184,16 +188,18 @@ using cql3::native_cql3_type; return res; } - public void addRawUpdate(List> operations, ColumnIdentifier.Raw key, Operation.RawUpdate update) - { - for (Pair p : operations) - { - if (p.left.equals(key) && !p.right.isCompatibleWith(update)) - addRecognitionError("Multiple incompatible setting of column " + key); - } - operations.add(Pair.create(key, update)); - } #endif + void add_raw_update(std::vector,::shared_ptr>>& operations, + ::shared_ptr key, ::shared_ptr update) + { + for (auto&& p : operations) { + if (*p.first == *key && !p.second->is_compatible_with(update)) { + // \%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)); + } } @lexer::namespace{cql3_parser} @@ -261,8 +267,8 @@ cqlStatement returns [shared_ptr stmt] @after{ if (stmt) { stmt->set_bound_variables(_bind_variables); } } : st1= selectStatement { $stmt = st1; } | st2= insertStatement { $stmt = st2; } -#if 0 | st3= updateStatement { $stmt = st3; } +#if 0 | st4= batchStatement { $stmt = st4; } | st5= deleteStatement { $stmt = st5; } #endif @@ -382,12 +388,13 @@ countArgument : '\*' | i=INTEGER { if (!i.getText().equals("1")) addRecognitionError("Only COUNT(1) is supported, got COUNT(" + i.getText() + ")");} ; +#endif -whereClause returns [List clause] - @init{ $clause = new ArrayList(); } +whereClause returns [std::vector clause] : relation[$clause] (K_AND relation[$clause])* ; +#if 0 orderByClause[Map orderings] @init{ boolean reversed = false; @@ -434,18 +441,16 @@ usingClauseObjective[::shared_ptr attrs] | K_TTL t=intValue { attrs->time_to_live = t; } ; -#if 0 - /** * UPDATE * USING TIMESTAMP * SET name1 = value1, name2 = value2 * WHERE key = value; */ -updateStatement returns [UpdateStatement.ParsedUpdate expr] +updateStatement returns [::shared_ptr expr] @init { - Attributes.Raw attrs = new Attributes.Raw(); - List> operations = new ArrayList>(); + auto attrs = ::make_shared(); + std::vector, ::shared_ptr>> operations; } : K_UPDATE cf=columnFamilyName ( usingClause[attrs] )? @@ -453,19 +458,19 @@ updateStatement returns [UpdateStatement.ParsedUpdate expr] K_WHERE wclause=whereClause ( K_IF conditions=updateConditions )? { - return new UpdateStatement.ParsedUpdate(cf, - attrs, - operations, - wclause, - conditions == null ? Collections.>emptyList() : conditions); + return ::make_shared(std::move(cf), + std::move(attrs), + std::move(operations), + std::move(wclause), + std::move(conditions)); } ; -updateConditions returns [List> conditions] - @init { conditions = new ArrayList>(); } +updateConditions returns [conditions_type conditions] : columnCondition[conditions] ( K_AND columnCondition[conditions] )* ; +#if 0 /** * DELETE name1, name2 @@ -1140,30 +1145,33 @@ term returns [::shared_ptr term] #endif ; -#if 0 -columnOperation[List> operations] +columnOperation[operations_type& operations] : key=cident columnOperationDifferentiator[operations, key] ; -columnOperationDifferentiator[List> operations, ColumnIdentifier.Raw key] +columnOperationDifferentiator[operations_type& operations, ::shared_ptr key] : '=' normalColumnOperation[operations, key] +#if 0 | '[' k=term ']' specializedColumnOperation[operations, key, k] +#endif ; -normalColumnOperation[List> operations, ColumnIdentifier.Raw key] +normalColumnOperation[operations_type& operations, ::shared_ptr key] : t=term ('+' c=cident )? { - if (c == null) - { - addRawUpdate(operations, key, new Operation.SetValue(t)); - } - else - { - if (!key.equals(c)) - addRecognitionError("Only expressions of the form X = + X are supported."); - addRawUpdate(operations, key, new Operation.Prepend(t)); + if (!c) { + add_raw_update(operations, key, ::make_shared(t)); + } else { + throw std::runtime_error("not implemented"); +#if 0 + if (!key.equals(c)) { + add_recognition_error("Only expressions of the form X = + X are supported."); + } + add_raw_update(operations, key, ::make_shared(t)); +#endif } } +#if 0 | c=cident sig=('+' | '-') t=term { if (!key.equals(c)) @@ -1178,33 +1186,42 @@ normalColumnOperation[List> oper addRecognitionError("Only expressions of the form X = X " + ($i.text.charAt(0) == '-' ? '-' : '+') + " are supported."); addRawUpdate(operations, key, new Operation.Addition(Constants.Literal.integer($i.text))); } +#endif ; +#if 0 specializedColumnOperation[List> operations, ColumnIdentifier.Raw key, Term.Raw k] : '=' t=term { addRawUpdate(operations, key, new Operation.SetElement(k, t)); } ; +#endif -columnCondition[List> conditions] +columnCondition[conditions_type& conditions] // Note: we'll reject duplicates later : key=cident - ( op=relationType t=term { conditions.add(Pair.create(key, ColumnCondition.Raw.simpleCondition(t, op))); } + ( op=relationType t=term { conditions.emplace_back(key, cql3::column_condition::raw::simple_condition(t, *op)); } | K_IN - ( values=singleColumnInValues { conditions.add(Pair.create(key, ColumnCondition.Raw.simpleInCondition(values))); } + ( values=singleColumnInValues { conditions.emplace_back(key, cql3::column_condition::raw::simple_in_condition(values)); } +#if 0 | marker=inMarker { conditions.add(Pair.create(key, ColumnCondition.Raw.simpleInCondition(marker))); } +#endif ) +#if 0 | '[' element=term ']' - ( op=relationType t=term { conditions.add(Pair.create(key, ColumnCondition.Raw.collectionCondition(t, element, op))); } + ( op=relationType t=term { conditions.add(Pair.create(key, ColumnCondition.Raw.collectionCondition(t, element, *op))); } | K_IN ( values=singleColumnInValues { conditions.add(Pair.create(key, ColumnCondition.Raw.collectionInCondition(element, values))); } | marker=inMarker { conditions.add(Pair.create(key, ColumnCondition.Raw.collectionInCondition(element, marker))); } ) ) +#endif ) ; +#if 0 + properties[PropertyDefinitions props] : property[props] (K_AND property[props])* ; @@ -1218,18 +1235,20 @@ propertyValue returns [String str] : c=constant { $str = c.getRawText(); } | u=unreserved_keyword { $str = u; } ; +#endif -relationType returns [Operator op] - : '=' { $op = Operator.EQ; } - | '<' { $op = Operator.LT; } - | '<=' { $op = Operator.LTE; } - | '>' { $op = Operator.GT; } - | '>=' { $op = Operator.GTE; } - | '!=' { $op = Operator.NEQ; } +relationType returns [const cql3::operator_type* op = nullptr] + : '=' { $op = &cql3::operator_type::EQ; } + | '<' { $op = &cql3::operator_type::LT; } + | '<=' { $op = &cql3::operator_type::LTE; } + | '>' { $op = &cql3::operator_type::GT; } + | '>=' { $op = &cql3::operator_type::GTE; } + | '!=' { $op = &cql3::operator_type::NEQ; } ; -relation[List clauses] - : name=cident type=relationType t=term { $clauses.add(new SingleColumnRelation(name, type, t)); } +relation[std::vector& clauses] + : name=cident type=relationType t=term { $clauses.emplace_back(::make_shared(std::move(name), *type, std::move(t))); } +#if 0 | K_TOKEN l=tupleOfIdentifiers type=relationType t=term { $clauses.add(new TokenRelation(l, type, t)); } | name=cident K_IN marker=inMarker @@ -1259,9 +1278,11 @@ relation[List clauses] | type=relationType tupleMarker=markerForTuple /* (a, b, c) >= ? */ { $clauses.add(MultiColumnRelation.createNonInRelation(ids, type, tupleMarker)); } ) +#endif | '(' relation[$clauses] ')' ; +#if 0 inMarker returns [AbstractMarker.INRaw marker] : QMARK { $marker = newINBindVariables(null); } | ':' name=ident { $marker = newINBindVariables(name); } @@ -1271,12 +1292,13 @@ tupleOfIdentifiers returns [List ids] @init { $ids = new ArrayList(); } : '(' n1=cident { $ids.add(n1); } (',' ni=cident { $ids.add(ni); })* ')' ; +#endif -singleColumnInValues returns [List terms] - @init { $terms = new ArrayList(); } - : '(' ( t1 = term { $terms.add(t1); } (',' ti=term { $terms.add(ti); })* )? ')' +singleColumnInValues returns [std::vector<::shared_ptr> terms] + : '(' ( t1 = term { $terms.push_back(t1); } (',' ti=term { $terms.push_back(ti); })* )? ')' ; +#if 0 tupleOfTupleLiterals returns [List literals] @init { $literals = new ArrayList<>(); } : '(' t1=tupleLiteral { $literals.add(t1); } (',' ti=tupleLiteral { $literals.add(ti); })* ')' From ebb8a112068866c434619a193faf20e102177f96 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 16:52:50 +0100 Subject: [PATCH 51/52] types: Add helper tuple_type::serialize_value_deep() It works on fully deserialized values. --- tuple.hh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tuple.hh b/tuple.hh index 0e8a95e4cf..bfe2235a2f 100644 --- a/tuple.hh +++ b/tuple.hh @@ -59,6 +59,16 @@ public: bytes serialize_value(const value_type& values) { return ::serialize_value(*this, values); } + bytes serialize_value_deep(const std::vector& values) { + // TODO: Optimize + std::vector partial; + auto i = types.begin(); + for (auto&& component : values) { + assert(i != types.end()); + partial.push_back({(*i++)->decompose(component)}); + } + return serialize_value(partial); + } bytes decompose_value(const value_type& values) { return ::serialize_value(*this, values); } From 6a998e2d784a279338b050eac5e9470d3148db8e Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Thu, 12 Feb 2015 18:27:17 +0100 Subject: [PATCH 52/52] tests: Add test for parsing and execution of CQL3 queries --- configure.py | 1 + test.py | 1 + tests/urchin/cql_query_test.cc | 83 ++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 tests/urchin/cql_query_test.cc diff --git a/configure.py b/configure.py index eb9a19cb9a..0f78947733 100755 --- a/configure.py +++ b/configure.py @@ -103,6 +103,7 @@ urchin_tests = [ 'tests/urchin/mutation_test', 'tests/urchin/types_test', 'tests/perf/perf_mutation', + 'tests/urchin/cql_query_test', ] tests = [ diff --git a/test.py b/test.py index 7cdd4f93fc..2df85b4560 100755 --- a/test.py +++ b/test.py @@ -12,6 +12,7 @@ all_tests = [ 'output_stream_test', 'urchin/types_test', 'urchin/mutation_test', + 'urchin/cql_query_test', ] last_len = 0 diff --git a/tests/urchin/cql_query_test.cc b/tests/urchin/cql_query_test.cc new file mode 100644 index 0000000000..8ee4e72786 --- /dev/null +++ b/tests/urchin/cql_query_test.cc @@ -0,0 +1,83 @@ +/* + * Copyright 2015 Cloudius Systems + */ + +#include +#include "cql3/query_processor.hh" +#include "cql3/query_options.hh" +#include "tests/test-utils.hh" +#include "core/future-util.hh" + +struct conversation_state { + service::storage_proxy proxy; + cql3::query_processor qp; + service::client_state client_state; + service::query_state query_state; + cql3::query_options& options; + + conversation_state(database& db, const sstring& ks_name) + : proxy(db) + , qp(proxy, db) + , query_state(client_state) + , options(cql3::query_options::DEFAULT) + { + client_state.set_keyspace(ks_name); + } + + future<> execute_cql(const sstring& text) { + return qp.process(text, query_state, options).discard_result(); + } +}; + +static const sstring ks_name = "ks"; +static const sstring table_name = "cf"; + +static void require_column_has_value(database& db, const sstring& ks_name, const sstring& table_name, + std::vector pk, std::vector ck, const sstring& column_name, boost::any expected) +{ + auto ks = db.find_keyspace(ks_name); + BOOST_REQUIRE(ks != nullptr); + auto cf = ks->find_column_family(table_name); + BOOST_REQUIRE(cf != nullptr); + auto schema = cf->_schema; + auto p = cf->find_partition(schema->partition_key_type->serialize_value_deep(pk)); + BOOST_REQUIRE(p != nullptr); + auto row = p->find_row(schema->clustering_key_type->serialize_value_deep(ck)); + BOOST_REQUIRE(row != nullptr); + auto col_def = schema->get_column_definition(utf8_type->decompose(column_name)); + BOOST_REQUIRE(col_def != nullptr); + auto i = row->find(col_def->id); + if (i == row->end()) { + BOOST_FAIL("column not set"); + } + auto& cell = boost::any_cast(i->second); + BOOST_REQUIRE(cell.is_live()); + BOOST_REQUIRE(col_def->type->equal(cell.as_live().value, col_def->type->decompose(expected))); +} + +SEASTAR_TEST_CASE(test_insert_statement) { + auto db = make_shared(); + + // CQL: create table cf (p1 varchar, c1 int, r1 int, PRIMARY KEY (p1, c1)); + keyspace ks; + auto cf_schema = make_lw_shared(ks_name, table_name, + std::vector({{"p1", utf8_type}}), + std::vector({{"c1", int32_type}}), + std::vector({{"r1", int32_type}}), + utf8_type + ); + ks.column_families.emplace(table_name, column_family(cf_schema)); + db->keyspaces.emplace(ks_name, std::move(ks)); + + auto state = make_shared(*db, ks_name); + + return now().then([state, db] { + return state->execute_cql("insert into cf (p1, c1, r1) values ('key1', 1, 100);"); + }).then([state, db] { + require_column_has_value(*db, ks_name, table_name, {sstring("key1")}, {1}, "r1", 100); + }).then([state, db] { + return state->execute_cql("update cf set r1 = 66 where p1 = 'key1' and c1 = 1;"); + }).then([state, db] { + require_column_has_value(*db, ks_name, table_name, {sstring("key1")}, {1}, "r1", 66); + }); +}