diff --git a/configure.py b/configure.py index 4de8b170a1..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 = [ @@ -248,6 +249,11 @@ urchin_core = (['database.cc', 'cql3/operator.cc', 'cql3/relation.cc', 'cql3/column_identifier.cc', + 'cql3/constants.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/Cql.g b/cql3/Cql.g index 256a61cbff..b0f66c8874 100644 --- a/cql3/Cql.g +++ b/cql3/Cql.g @@ -23,13 +23,21 @@ options { language = Cpp; } -@parser::namespace{cql3} +@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/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" @@ -39,9 +47,12 @@ options { } @parser::traits { -using namespace cql3; 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 { @@ -74,11 +85,13 @@ using namespace cql3::selection; #endif } -@members { -#if 0 - private final List listeners = new ArrayList(); - private final List bindVariables = new ArrayList(); +@context { + using listener_type = cql3::error_listener; + listener_type* listener; + std::vector<::shared_ptr> _bind_variables; + +#if 0 public static final Set reservedTypeNames = new HashSet() {{ add("byte"); @@ -118,29 +131,25 @@ using namespace cql3::selection; 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()) @@ -179,19 +188,21 @@ using namespace cql3::selection; 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} +@lexer::namespace{cql3_parser} @lexer::traits { class CqlLexer; @@ -210,7 +221,7 @@ using namespace cql3::selection; #endif } -@lexer::members { +@lexer::context { #if 0 List tokens = new ArrayList(); @@ -227,19 +238,17 @@ using namespace cql3::selection; 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++) @@ -255,13 +264,11 @@ 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; } -#if 0 | st2= insertStatement { $stmt = st2; } | st3= updateStatement { $stmt = st3; } +#if 0 | st4= batchStatement { $stmt = st4; } | st5= deleteStatement { $stmt = st5; } #endif @@ -346,7 +353,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); } ; @@ -381,18 +388,20 @@ 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; } : c=cident (K_ASC | K_DESC { reversed = true; })? { orderings.put(c, reversed); } ; +#endif /** * INSERT INTO (, , , ...) @@ -400,36 +409,36 @@ 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; } ; /** @@ -438,10 +447,10 @@ usingClauseObjective[Attributes.Raw attrs] * 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] )? @@ -449,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 @@ -981,22 +990,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 +1022,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 +1033,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 +1042,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 @@ -1082,21 +1091,25 @@ 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] +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); } @@ -1122,36 +1135,43 @@ 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 ; -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)) @@ -1166,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])* ; @@ -1206,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 @@ -1247,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); } @@ -1259,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); })* ')' 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/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); } } 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) { 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); }; }; 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; diff --git a/cql3/constants.cc b/cql3/constants.cc new file mode 100644 index 0000000000..834a2a15f4 --- /dev/null +++ b/cql3/constants.cc @@ -0,0 +1,139 @@ +/* + * 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" + +namespace cql3 { + +const ::shared_ptr constants::NULL_LITERAL = ::make_shared(); +const ::shared_ptr constants::null_literal::NULL_VALUE = ::make_shared(); + +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 d73dac31d5..5f45a0ed29 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: @@ -142,153 +145,21 @@ 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; } }; -#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) @@ -409,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 diff --git a/cql3/cql_statement.hh b/cql3/cql_statement.hh index 1c6de1a138..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. @@ -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/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; +}; + } 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 /** 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 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/cql3/relation.hh b/cql3/relation.hh index 93b09c4065..a9c8216ddb 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() {} @@ -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 == 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/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/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.cc b/cql3/statements/modification_statement.cc index da880e7ae7..7497957830 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) { @@ -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 @@ -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: @@ -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 dc01ea8be9..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; } @@ -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,22 +199,9 @@ public: if (hasConditions()) state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.SELECT); #endif - 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()) { @@ -295,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"); } @@ -307,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 @@ -466,7 +454,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..89c7f0a558 100644 --- a/cql3/statements/schema_altering_statement.hh +++ b/cql3/statements/schema_altering_statement.hh @@ -45,17 +45,17 @@ 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} { } - virtual int get_bound_terms() override { + virtual uint32_t get_bound_terms() override { return 0; } @@ -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 f376e281b1..979814540f 100644 --- a/cql3/statements/truncate_statement.hh +++ b/cql3/statements/truncate_statement.hh @@ -36,11 +36,11 @@ 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)} { } - virtual int get_bound_terms() override { + virtual uint32_t get_bound_terms() override { return 0; } @@ -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/update_statement.hh b/cql3/statements/update_statement.hh index d2cad8c09d..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)} { } @@ -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; @@ -89,14 +89,14 @@ 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, 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, @@ -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, diff --git a/cql3/statements/use_statement.hh b/cql3/statements/use_statement.hh index c641fabcdb..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; } @@ -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"); diff --git a/cql3/term.hh b/cql3/term.hh index 93c919be38..6be55d3fe2 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. * @@ -160,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 { diff --git a/database.cc b/database.cc index e26c127e67..e788f14d24 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" @@ -229,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; @@ -238,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 {}; @@ -247,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; @@ -297,7 +298,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 +335,56 @@ 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; +} + +bool column_definition::is_compact_value() const { + unimplemented::compact_tables(); + return false; +} diff --git a/database.hh b/database.hh index 197c52a19d..04395a8ccd 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; @@ -157,81 +156,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 { @@ -280,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); }; 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; + } + }; +}; 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(); +} diff --git a/exceptions/exceptions.hh b/exceptions/exceptions.hh index 687e104f05..74dafa2469 100644 --- a/exceptions/exceptions.hh +++ b/exceptions/exceptions.hh @@ -1,7 +1,32 @@ +/* + * 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 #include +#include "core/sstring.hh" namespace exceptions { @@ -26,6 +51,66 @@ 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 +}; + +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)) + { } +}; + +class recognition_exception : public std::runtime_error { +public: + recognition_exception(const std::string& msg) : std::runtime_error(msg) {}; +}; + } #endif 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/service/client_state.hh b/service/client_state.hh index c1f3583287..5e4c8efecb 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()). @@ -181,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"); } diff --git a/service/pager/paging_state.hh b/service/pager/paging_state.hh new file mode 100644 index 0000000000..8857e8392c --- /dev/null +++ b/service/pager/paging_state.hh @@ -0,0 +1,96 @@ +/* + * 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 + */ + +#pragma once + +namespace service { + +namespace pager { + +class paging_state final { +#if 0 + 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); + } +#endif +}; + +} + +} 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(); + } }; } 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); + }); +} 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. 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/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<>(); diff --git a/tuple.hh b/tuple.hh index 33e4c7444f..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); } @@ -162,7 +172,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 { @@ -214,6 +224,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 82ad5fec09..9c5cca7744 100644 --- a/types.cc +++ b/types.cc @@ -3,6 +3,7 @@ */ #include +#include "cql3/cql3_type.hh" #include "types.hh" template @@ -66,14 +67,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 { @@ -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 { @@ -103,16 +107,20 @@ 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 { 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()); @@ -135,15 +143,19 @@ 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 { 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); @@ -166,12 +178,15 @@ 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 { 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 { @@ -189,12 +204,15 @@ 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 { 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 { @@ -226,12 +244,15 @@ 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 { 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 { @@ -272,12 +293,15 @@ 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 { 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) { @@ -315,12 +339,15 @@ 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 { 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 839b2f0233..c9b2e93e0b 100644 --- a/types.hh +++ b/types.hh @@ -16,10 +16,17 @@ #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; 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); @@ -113,10 +120,11 @@ 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; } + virtual ::shared_ptr as_cql3_type() = 0; protected: template > bool default_less(const bytes& b1, const bytes& b2, Compare compare = Compare()); @@ -193,6 +201,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) { diff --git a/unimplemented.hh b/unimplemented.hh index 04a80af642..6a64dcdcc2 100644 --- a/unimplemented.hh +++ b/unimplemented.hh @@ -40,9 +40,28 @@ void auth() { warn("auth"); } +static inline +void permissions() { + warn("permissions"); +} + static inline void triggers() { warn("triggers"); } +static inline +void collections() __attribute__((noreturn)); + +static inline +void collections() { + fail("collections"); +} + +static inline +void metrics() {} + +static inline +void compact_tables() {} + }