Merge branch 'tgrabiec/cql3' of github.com:cloudius-systems/seastar-dev into db

Enable parsing of INSERT and UPDATE CQL3 statements, from Tomasz.
This commit is contained in:
Avi Kivity
2015-02-15 09:52:00 +02:00
50 changed files with 1822 additions and 1055 deletions

View File

@@ -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',

View File

@@ -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<std::pair<::shared_ptr<cql3::column_identifier::raw>,::shared_ptr<cql3::column_condition::raw>>>;
using operations_type = std::vector<std::pair<::shared_ptr<cql3::column_identifier::raw>,::shared_ptr<cql3::operation::raw_update>>>;
}
@header {
@@ -74,11 +85,13 @@ using namespace cql3::selection;
#endif
}
@members {
#if 0
private final List<ErrorListener> listeners = new ArrayList<ErrorListener>();
private final List<ColumnIdentifier> bindVariables = new ArrayList<ColumnIdentifier>();
@context {
using listener_type = cql3::error_listener<RecognizerType>;
listener_type* listener;
std::vector<::shared_ptr<cql3::column_identifier>> _bind_variables;
#if 0
public static final Set<String> reservedTypeNames = new HashSet<String>()
{{
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<String, String> 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<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key, Operation.RawUpdate update)
{
for (Pair<ColumnIdentifier.Raw, Operation.RawUpdate> 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<std::pair<::shared_ptr<cql3::column_identifier::raw>,::shared_ptr<cql3::operation::raw_update>>>& operations,
::shared_ptr<cql3::column_identifier::raw> key, ::shared_ptr<cql3::operation::raw_update> 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<Token> tokens = new ArrayList<Token>();
@@ -227,19 +238,17 @@ using namespace cql3::selection;
return new CommonToken(Token.EOF);
return tokens.remove(0);
}
#endif
private final List<ErrorListener> listeners = new ArrayList<ErrorListener>();
using listener_type = cql3::error_listener<RecognizerType>;
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<parsed_statement> stmnt]
;
cqlStatement returns [shared_ptr<parsed_statement> 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<shared_ptr<raw_selector>> expr]
;
selector returns [shared_ptr<raw_selector> s]
@init{ shared_ptr<column_identifier> alias; }
@init{ shared_ptr<cql3::column_identifier> alias; }
: us=unaliasedSelector (K_AS c=ident { alias = c; })? { $s = make_shared<raw_selector>(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<Relation> clause]
@init{ $clause = new ArrayList<Relation>(); }
whereClause returns [std::vector<cql3::relation_ptr> clause]
: relation[$clause] (K_AND relation[$clause])*
;
#if 0
orderByClause[Map<ColumnIdentifier.Raw, Boolean> orderings]
@init{
boolean reversed = false;
}
: c=cident (K_ASC | K_DESC { reversed = true; })? { orderings.put(c, reversed); }
;
#endif
/**
* INSERT INTO <CF> (<column>, <column>, <column>, ...)
@@ -400,36 +409,36 @@ orderByClause[Map<ColumnIdentifier.Raw, Boolean> orderings]
* USING TIMESTAMP <long>;
*
*/
insertStatement returns [UpdateStatement.ParsedInsert expr]
insertStatement returns [::shared_ptr<update_statement::parsed_insert> expr]
@init {
Attributes.Raw attrs = new Attributes.Raw();
List<ColumnIdentifier.Raw> columnNames = new ArrayList<ColumnIdentifier.Raw>();
List<Term.Raw> values = new ArrayList<Term.Raw>();
boolean ifNotExists = false;
auto attrs = ::make_shared<cql3::attributes::raw>();
std::vector<::shared_ptr<cql3::column_identifier::raw>> column_names;
std::vector<::shared_ptr<cql3::term::raw>> values;
bool if_not_exists = false;
}
: 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<update_statement::parsed_insert>(std::move(cf),
std::move(attrs),
std::move(column_names),
std::move(values),
if_not_exists);
}
;
usingClause[Attributes.Raw attrs]
usingClause[::shared_ptr<cql3::attributes::raw> 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<cql3::attributes::raw> 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<update_statement::parsed_update> expr]
@init {
Attributes.Raw attrs = new Attributes.Raw();
List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations = new ArrayList<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>>();
auto attrs = ::make_shared<cql3::attributes::raw>();
std::vector<std::pair<::shared_ptr<cql3::column_identifier::raw>, ::shared_ptr<cql3::operation::raw_update>>> 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.<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>>emptyList() : conditions);
return ::make_shared<update_statement::parsed_update>(std::move(cf),
std::move(attrs),
std::move(operations),
std::move(wclause),
std::move(conditions));
}
;
updateConditions returns [List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions]
@init { conditions = new ArrayList<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>>(); }
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<column_identifier::raw> id]
: t=IDENT { $id = make_shared<column_identifier::raw>(sstring{$t.text}, false); }
| t=QUOTED_NAME { $id = make_shared<column_identifier::raw>(sstring{$t.text}, true); }
| k=unreserved_keyword { $id = make_shared<column_identifier::raw>(k, false); }
cident returns [shared_ptr<cql3::column_identifier::raw> id]
: t=IDENT { $id = make_shared<cql3::column_identifier::raw>(sstring{$t.text}, false); }
| t=QUOTED_NAME { $id = make_shared<cql3::column_identifier::raw>(sstring{$t.text}, true); }
| k=unreserved_keyword { $id = make_shared<cql3::column_identifier::raw>(k, false); }
;
// Identifiers that do not refer to columns or where the comparator is known to be text
ident returns [shared_ptr<column_identifier> id]
: t=IDENT { $id = make_shared<column_identifier>(sstring{$t.text}, false); }
| t=QUOTED_NAME { $id = make_shared<column_identifier>(sstring{$t.text}, true); }
| k=unreserved_keyword { $id = make_shared<column_identifier>(k, false); }
ident returns [shared_ptr<cql3::column_identifier> id]
: t=IDENT { $id = make_shared<cql3::column_identifier>(sstring{$t.text}, false); }
| t=QUOTED_NAME { $id = make_shared<cql3::column_identifier>(sstring{$t.text}, true); }
| k=unreserved_keyword { $id = make_shared<cql3::column_identifier>(k, false); }
;
// Keyspace & Column family names
keyspaceName returns [sstring id]
@init { auto name = make_shared<cf_name>(); }
@init { auto name = make_shared<cql3::cf_name>(); }
: cfOrKsName[name, true] { $id = name->get_keyspace(); }
;
@@ -1013,8 +1022,8 @@ idxOrKsName[IndexName name, boolean isKs]
;
#endif
columnFamilyName returns [shared_ptr<cf_name> name]
@init { $name = make_shared<cf_name>(); }
columnFamilyName returns [shared_ptr<cql3::cf_name> name]
@init { $name = make_shared<cql3::cf_name>(); }
: (cfOrKsName[name, true] '.')? cfOrKsName[name, false]
;
@@ -1024,7 +1033,7 @@ userTypeName returns [UTName name]
;
#endif
cfOrKsName[shared_ptr<cf_name> name, bool isKs]
cfOrKsName[shared_ptr<cql3::cf_name> 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<cf_name> name, bool isKs]
#endif
;
constant returns [shared_ptr<constants::literal> constant]
constant returns [shared_ptr<cql3::constants::literal> 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<cql3::term::raw> 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<term::raw> value]
intValue returns [::shared_ptr<cql3::term::raw> 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<Term.Raw> a]
( ',' tn=term { args.add(tn); } )*
')' { $a = args; }
;
#endif
term returns [Term.Raw term]
term returns [::shared_ptr<cql3::term::raw> 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<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations]
columnOperation[operations_type& operations]
: key=cident columnOperationDifferentiator[operations, key]
;
columnOperationDifferentiator[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key]
columnOperationDifferentiator[operations_type& operations, ::shared_ptr<cql3::column_identifier::raw> key]
: '=' normalColumnOperation[operations, key]
#if 0
| '[' k=term ']' specializedColumnOperation[operations, key, k]
#endif
;
normalColumnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key]
normalColumnOperation[operations_type& operations, ::shared_ptr<cql3::column_identifier::raw> 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 = <value> + X are supported.");
addRawUpdate(operations, key, new Operation.Prepend(t));
if (!c) {
add_raw_update(operations, key, ::make_shared<cql3::operation::set_value>(t));
} else {
throw std::runtime_error("not implemented");
#if 0
if (!key.equals(c)) {
add_recognition_error("Only expressions of the form X = <value> + X are supported.");
}
add_raw_update(operations, key, ::make_shared<cql3::operation::prepend>(t));
#endif
}
}
#if 0
| c=cident sig=('+' | '-') t=term
{
if (!key.equals(c))
@@ -1166,33 +1186,42 @@ normalColumnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> oper
addRecognitionError("Only expressions of the form X = X " + ($i.text.charAt(0) == '-' ? '-' : '+') + " <value> are supported.");
addRawUpdate(operations, key, new Operation.Addition(Constants.Literal.integer($i.text)));
}
#endif
;
#if 0
specializedColumnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key, Term.Raw k]
: '=' t=term
{
addRawUpdate(operations, key, new Operation.SetElement(k, t));
}
;
#endif
columnCondition[List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> 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<Relation> clauses]
: name=cident type=relationType t=term { $clauses.add(new SingleColumnRelation(name, type, t)); }
relation[std::vector<cql3::relation_ptr>& clauses]
: name=cident type=relationType t=term { $clauses.emplace_back(::make_shared<cql3::single_column_relation>(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<Relation> 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<ColumnIdentifier.Raw> ids]
@init { $ids = new ArrayList<ColumnIdentifier.Raw>(); }
: '(' n1=cident { $ids.add(n1); } (',' ni=cident { $ids.add(ni); })* ')'
;
#endif
singleColumnInValues returns [List<Term.Raw> terms]
@init { $terms = new ArrayList<Term.Raw>(); }
: '(' ( t1 = term { $terms.add(t1); } (',' ti=term { $terms.add(ti); })* )? ')'
singleColumnInValues returns [std::vector<::shared_ptr<cql3::term::raw>> terms]
: '(' ( t1 = term { $terms.push_back(t1); } (',' ti=term { $terms.push_back(ti); })* )? ')'
;
#if 0
tupleOfTupleLiterals returns [List<Tuples.Literal> literals]
@init { $literals = new ArrayList<>(); }
: '(' t1=tupleLiteral { $literals.add(t1); } (',' ti=tupleLiteral { $literals.add(ti); })* ')'

View File

@@ -97,6 +97,7 @@ public:
return ::make_shared<column_specification>(receiver->ks_name, receiver->cf_name, in_name, db::marshal::list_type::get_instance(receiver->type, false));
}
public:
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
};
};

View File

@@ -38,24 +38,24 @@ namespace cql3 {
*/
class attributes final {
private:
const std::experimental::optional<::shared_ptr<term>> _timestamp;
const std::experimental::optional<::shared_ptr<term>> _time_to_live;
const ::shared_ptr<term> _timestamp;
const ::shared_ptr<term> _time_to_live;
public:
static std::unique_ptr<attributes> none() {
return std::unique_ptr<attributes>{new attributes{std::move(std::experimental::optional<::shared_ptr<term>>{}), std::move(std::experimental::optional<::shared_ptr<term>>{})}};
return std::unique_ptr<attributes>{new attributes{{}, {}}};
}
private:
attributes(std::experimental::optional<::shared_ptr<term>>&& timestamp, std::experimental::optional<::shared_ptr<term>>&& time_to_live)
attributes(::shared_ptr<term>&& timestamp, ::shared_ptr<term>&& 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<variable_specifications> 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);
}
}

View File

@@ -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) {

120
cql3/column_condition.cc Normal file
View File

@@ -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<variable_specifications> 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>
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<term>> 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<Term> 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
}
}

View File

@@ -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<Term> inValues;
public final Operator operator;
private ColumnCondition(ColumnDefinition column, Term collectionElement, Term value, List<Term> inValues, Operator op)
private:
// For collection, when testing the equality of a specific element, nullptr otherwise.
::shared_ptr<term> _collection_element;
::shared_ptr<term> _value;
std::vector<::shared_ptr<term>> _in_values;
const operator_type& _op;
public:
column_condition(column_definition& column, ::shared_ptr<term> collection_element,
::shared_ptr<term> value, std::vector<::shared_ptr<term>> 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<column_condition> condition(column_definition& def, ::shared_ptr<term> value, const operator_type& op) {
return ::make_shared<column_condition>(def, ::shared_ptr<term>{}, std::move(value), std::vector<::shared_ptr<term>>{}, op);
}
public static ColumnCondition condition(ColumnDefinition column, Term collectionElement, Term value, Operator op)
{
return new ColumnCondition(column, collectionElement, value, null, op);
static ::shared_ptr<column_condition> condition(column_definition& def, ::shared_ptr<term> collection_element,
::shared_ptr<term> value, const operator_type& op) {
return ::make_shared<column_condition>(def, std::move(collection_element), std::move(value),
std::vector<::shared_ptr<term>>{}, op);
}
public static ColumnCondition inCondition(ColumnDefinition column, List<Term> inValues)
{
return new ColumnCondition(column, null, null, inValues, Operator.IN);
static ::shared_ptr<column_condition> in_condition(column_definition& def, std::vector<::shared_ptr<term>> in_values) {
return ::make_shared<column_condition>(def, ::shared_ptr<term>{}, ::shared_ptr<term>{},
std::move(in_values), operator_type::IN);
}
public static ColumnCondition inCondition(ColumnDefinition column, Term collectionElement, List<Term> inValues)
{
return new ColumnCondition(column, collectionElement, null, inValues, Operator.IN);
static ::shared_ptr<column_condition> in_condition(column_definition& def, ::shared_ptr<term> collection_element,
std::vector<::shared_ptr<term>> in_values) {
return ::make_shared<column_condition>(def, std::move(collection_element), ::shared_ptr<term>{},
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<column_condition> in_condition(column_definition& def, ::shared_ptr<term> in_marker) {
return ::make_shared<column_condition>(def, ::shared_ptr<term>{}, std::move(in_marker),
std::vector<::shared_ptr<term>>{}, 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<column_condition> in_condition(column_definition& def, ::shared_ptr<term> collection_element,
::shared_ptr<term> in_marker) {
return ::make_shared<column_condition>(def, std::move(collection_element), std::move(in_marker),
std::vector<::shared_ptr<term>>{}, 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<variable_specifications> 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<variable_specifications> 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<Term.Raw> inValues;
private final AbstractMarker.INRaw inMarker;
class raw final {
private:
::shared_ptr<term::raw> _value;
std::vector<::shared_ptr<term::raw>> _in_values;
::shared_ptr<abstract_marker::in_raw> _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<Term.Raw> 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<term::raw> _collection_element;
const operator_type& _op;
public:
raw(::shared_ptr<term::raw> value,
std::vector<::shared_ptr<term::raw>> in_values,
::shared_ptr<abstract_marker::in_raw> in_marker,
::shared_ptr<term::raw> 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<raw> simple_condition(::shared_ptr<term::raw> value, const operator_type& op) {
return ::make_shared<raw>(std::move(value), std::vector<::shared_ptr<term::raw>>{},
::shared_ptr<abstract_marker::in_raw>{}, ::shared_ptr<term::raw>{}, op);
}
/** An IN condition on a column. For example: "IF col IN ('foo', 'bar', ...)" */
public static Raw simpleInCondition(List<Term.Raw> inValues)
{
return new Raw(null, inValues, null, null, Operator.IN);
static ::shared_ptr<raw> simple_in_condition(std::vector<::shared_ptr<term::raw>> in_values) {
return ::make_shared<raw>(::shared_ptr<term::raw>{}, std::move(in_values),
::shared_ptr<abstract_marker::in_raw>{}, ::shared_ptr<term::raw>{}, 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<raw> simple_in_condition(::shared_ptr<abstract_marker::in_raw> in_marker) {
return ::make_shared<raw>(::shared_ptr<term::raw>{}, std::vector<::shared_ptr<term::raw>>{},
std::move(in_marker), ::shared_ptr<term::raw>{}, 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<raw> collection_condition(::shared_ptr<term::raw> value, ::shared_ptr<term::raw> collection_element,
const operator_type& op) {
return ::make_shared<raw>(std::move(value), std::vector<::shared_ptr<term::raw>>{}, ::shared_ptr<abstract_marker::in_raw>{}, 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<Term.Raw> inValues)
{
return new Raw(null, inValues, null, collectionElement, Operator.IN);
static ::shared_ptr<raw> collection_in_condition(::shared_ptr<term::raw> collection_element,
std::vector<::shared_ptr<term::raw>> in_values) {
return ::make_shared<raw>(::shared_ptr<term::raw>{}, std::move(in_values), ::shared_ptr<abstract_marker::in_raw>{},
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<raw> collectionInCondition(::shared_ptr<term::raw> collection_element,
::shared_ptr<abstract_marker::in_raw> in_marker) {
return ::make_shared<raw>(::shared_ptr<term::raw>{}, std::vector<::shared_ptr<term::raw>>{}, std::move(in_marker),
std::move(collection_element), operator_type::IN);
}
#endif
public:
::shared_ptr<column_condition> 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<Term> 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<Term> 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<column_condition> prepare(const sstring& keyspace, column_definition& receiver);
};
};

View File

@@ -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;

139
cql3/constants.cc Normal file
View File

@@ -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<term::raw> constants::NULL_LITERAL = ::make_shared<constants::null_literal>();
const ::shared_ptr<terminal> constants::null_literal::NULL_VALUE = ::make_shared<constants::null_literal::null_value>();
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<abstract_type> validator)
{
try {
if (_type == type::HEX && validator == bytes_type) {
auto v = static_cast<sstring_view>(_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<column_specification> 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<native_cql3_type*>(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<native_cql3_type::kind::BOOLEAN>()) {
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
}
break;
case type::HEX:
if (kind == native_cql3_type::kind_enum_set::prepare<native_cql3_type::kind::BLOB>()) {
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
}
break;
}
return assignment_testable::test_result::NOT_ASSIGNABLE;
}
::shared_ptr<term>
constants::literal::prepare(const sstring& keyspace, ::shared_ptr<column_specification> 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<value>(std::experimental::make_optional(parsed_value(receiver->type)));
}
}

View File

@@ -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<terminal> 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<terminal> NULL_VALUE;
public:
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> 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<column_specification> 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<term::raw> NULL_LITERAL;
class literal : public term::raw {
private:
@@ -142,153 +145,21 @@ public:
return ::make_shared<literal>(type::HEX, text);
}
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> 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<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver);
private:
bytes parsed_value(::shared_ptr<abstract_type> 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<column_specification> 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<column_specification> 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<column_specification> receiver)
@@ -409,6 +280,8 @@ public:
#endif
};
std::ostream& operator<<(std::ostream&out, constants::type t);
}
#endif

View File

@@ -6,18 +6,18 @@
namespace cql3 {
thread_local shared_ptr<cql3_type> native_cql3_type::ascii = make("ascii", ascii_type);
thread_local shared_ptr<cql3_type> native_cql3_type::bigint = make("bigint", long_type);
thread_local shared_ptr<cql3_type> native_cql3_type::blob = make("blob", bytes_type);
thread_local shared_ptr<cql3_type> native_cql3_type::boolean = make("boolean", boolean_type);
//thread_local shared_ptr<cql3_type> native_cql3_type::double = make("double", double_type);
//thread_local shared_ptr<cql3_type> native_cql3_type::float = make("float", float_type);
thread_local shared_ptr<cql3_type> native_cql3_type::int_ = make("int", int32_type);
thread_local shared_ptr<cql3_type> native_cql3_type::text = make("text", utf8_type);
thread_local shared_ptr<cql3_type> native_cql3_type::timestamp = make("timestamp", timestamp_type);
thread_local shared_ptr<cql3_type> native_cql3_type::uuid = make("uuid", uuid_type);
thread_local shared_ptr<cql3_type> native_cql3_type::varchar = make("varchar", utf8_type);
thread_local shared_ptr<cql3_type> native_cql3_type::timeuuid = make("timeuuid", timeuuid_type);
thread_local shared_ptr<cql3_type> native_cql3_type::ascii = make("ascii", ascii_type, native_cql3_type::kind::ASCII);
thread_local shared_ptr<cql3_type> native_cql3_type::bigint = make("bigint", long_type, native_cql3_type::kind::BIGINT);
thread_local shared_ptr<cql3_type> native_cql3_type::blob = make("blob", bytes_type, native_cql3_type::kind::BLOB);
thread_local shared_ptr<cql3_type> native_cql3_type::boolean = make("boolean", boolean_type, native_cql3_type::kind::BOOLEAN);
//thread_local shared_ptr<cql3_type> native_cql3_type::double = make("double", double_type, native_cql3_type::kind::DOUBLE);
//thread_local shared_ptr<cql3_type> native_cql3_type::float = make("float", float_type, native_cql3_type::kind::FLOAT);
thread_local shared_ptr<cql3_type> native_cql3_type::int_ = make("int", int32_type, native_cql3_type::kind::INT);
thread_local shared_ptr<cql3_type> native_cql3_type::text = make("text", utf8_type, native_cql3_type::kind::TEXT);
thread_local shared_ptr<cql3_type> native_cql3_type::timestamp = make("timestamp", timestamp_type, native_cql3_type::kind::TIMESTAMP);
thread_local shared_ptr<cql3_type> native_cql3_type::uuid = make("uuid", uuid_type, native_cql3_type::kind::UUID);
thread_local shared_ptr<cql3_type> native_cql3_type::varchar = make("varchar", utf8_type, native_cql3_type::kind::TEXT);
thread_local shared_ptr<cql3_type> native_cql3_type::timeuuid = make("timeuuid", timeuuid_type, native_cql3_type::kind::TIMEUUID);
const std::vector<shared_ptr<cql3_type>>&
native_cql3_type::values() {

View File

@@ -24,9 +24,10 @@
#pragma once
#include "database.hh"
#include "types.hh"
#include "exceptions/exceptions.hh"
#include <iosfwd>
#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<cql3_type> make(sstring name, data_type type) {
return make_shared<native_cql3_type>(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<kind,
kind::ASCII,
kind::BIGINT,
kind::BLOB,
kind::BOOLEAN,
kind::COUNTER,
kind::DECIMAL,
kind::DOUBLE,
kind::FLOAT,
kind::INET,
kind::INT,
kind::TEXT,
kind::TIMESTAMP,
kind::UUID,
kind::VARCHAR,
kind::VARINT,
kind::TIMEUUID>;
using kind_enum_set = enum_set<kind_enum>;
private:
kind_enum_set::prepared _kind;
static shared_ptr<cql3_type> make(sstring name, data_type type, kind kind_) {
return make_shared<native_cql3_type>(std::move(name), std::move(type), kind_);
}
public:
static thread_local shared_ptr<cql3_type> ascii;
@@ -67,10 +93,16 @@ public:
#endif
static const std::vector<shared_ptr<cql3_type>>& 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

View File

@@ -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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
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;

View File

@@ -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 {
/**
* <code>ErrorListener</code> that collect and enhance the errors send by the CQL lexer and parser.
*/
public final class ErrorCollector implements ErrorListener
{
template<typename Recognizer>
class error_collector : public error_listener<Recognizer> {
/**
* 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<String> errorMsgs = new LinkedList<>();
std::vector<sstring> _error_msgs;
public:
/**
* Creates a new <code>ErrorCollector</code> 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<sstring>& 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
};
}

View File

@@ -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 <vector>
#include "core/sstring.hh"
namespace cql3 {
/**
* Listener used to collect the syntax errors emitted by the Lexer and Parser.
*/
public interface ErrorListener
{
template<typename RecognizerType>
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<sstring>& 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;
};
}

View File

@@ -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
/**

34
cql3/query_options.cc Normal file
View File

@@ -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};
}

View File

@@ -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.<ByteBuffer>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<service::pager::paging_state> state;
const std::experimental::optional<db::consistency_level> 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<QueryOptions> codec = new Codec();
public static QueryOptions fromProtocolV1(ConsistencyLevel consistency, List<ByteBuffer> values)
@@ -76,39 +89,26 @@ public:
}
#endif
public:
virtual db::consistency_level get_consistency() const = 0;
#if 0
public abstract List<ByteBuffer> getValues();
public abstract boolean skipMetadata();
virtual const std::vector<bytes_opt>& 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<service::pager::paging_state> get_paging_state() const {
return get_specific_options().state;
}
/** Serial consistency for conditional updates. */
public ConsistencyLevel getSerialConsistency()
{
return getSpecificOptions().serialConsistency;
std::experimental::optional<db::consistency_level> 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<ColumnSpecification> specs)
{
return this;
}
static class DefaultQueryOptions extends QueryOptions
{
private final ConsistencyLevel consistency;
private final List<ByteBuffer> values;
private final boolean skipMetadata;
private final SpecificOptions options;
private final transient int protocolVersion;
DefaultQueryOptions(ConsistencyLevel consistency, List<ByteBuffer> 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<ByteBuffer> 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<column_specification>>& 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<QueryOptions>
{
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<bytes_opt> _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<bytes_opt> 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<bytes_opt>& 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

125
cql3/query_processor.cc Normal file
View File

@@ -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<result_message>>
query_processor::process(const sstring_view& query_string, service::query_state& query_state, query_options& options)
{
std::unique_ptr<parsed_statement::prepared> 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<result_message>>
query_processor::process_statement(::shared_ptr<cql_statement> 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<result_message>>(std::move(msg));
}
return make_ready_future<::shared_ptr<result_message>>(
::make_shared<result_message::void_message>());
});
}
std::unique_ptr<parsed_statement::prepared>
query_processor::get_statement(const sstring_view& query, service::client_state& client_state)
{
#if 0
Tracing.trace("Parsing {}", queryStr);
#endif
::shared_ptr<parsed_statement> statement = parse_statement(query);
// Set keyspace for statement that require login
auto cf_stmt = dynamic_pointer_cast<cf_statement>(statement);
if (cf_stmt) {
cf_stmt->prepare_keyspace(client_state);
}
#if 0
Tracing.trace("Preparing statement");
#endif
return statement->prepare(_db);
}
::shared_ptr<parsed_statement>
query_processor::parse_statement(const sstring_view& query)
{
try {
error_collector<cql3_parser::CqlLexer::RecognizerType> lexer_error_collector(query);
error_collector<cql3_parser::CqlParser::RecognizerType> parser_error_collector(query);
cql3_parser::CqlLexer::InputStreamType input{reinterpret_cast<const ANTLR_UINT8*>(query.begin()), ANTLR_ENC_UTF8, static_cast<ANTLR_UINT32>(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()));
}
}
}

View File

@@ -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 <experimental/string_view>
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<transport::messages::result_message>> process_statement(::shared_ptr<cql_statement> 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.<ByteBuffer>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<transport::messages::result_message>> 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<statements::parsed_statement::prepared> get_statement(const std::experimental::string_view& query,
service::client_state& client_state);
static ::shared_ptr<statements::parsed_statement> 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
};
}

View File

@@ -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 <code>true</code> if the operator of this relation is a <code>Slice</code>, <code>false</code> 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 ==

View File

@@ -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<term>
single_column_relation::to_term(std::vector<::shared_ptr<column_specification>> receivers,
::shared_ptr<term::raw> raw,
const sstring& keyspace,
::shared_ptr<variable_specifications> 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<restrictions::restriction>
single_column_relation::new_EQ_restriction(schema_ptr schema, ::shared_ptr<variable_specifications> 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<single_column_restriction::EQ>(column_def, std::move(term));
}
unimplemented::collections();
#if 0
List<? extends ColumnSpecification> 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<column_specification>>
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<ColumnSpecification> receivers = new ArrayList<>(2);
receivers.add(makeCollectionReceiver(receiver, true));
receivers.add(makeCollectionReceiver(receiver, false));
return receivers;
}
#endif
}
return {std::move(receiver)};
}
}

View File

@@ -77,7 +77,7 @@ public:
* @param value the value being compared.
*/
single_column_relation(::shared_ptr<column_identifier::raw> entity, const operator_type& type, ::shared_ptr<term::raw> 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<term::raw> get_map_key() {
return _map_key;
}
protected:
::shared_ptr<term> to_term(std::vector<::shared_ptr<column_specification>> receivers,
::shared_ptr<term::raw> raw, const sstring& keyspace,
::shared_ptr<variable_specifications> bound_names);
#if 0
@Override
protected Term toTerm(List<? extends ColumnSpecification> receivers,
Raw raw,
String keyspace,
::shared_ptr<variable_specifications> 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<restrictions::restriction> new_EQ_restriction(schema_ptr schema,
::shared_ptr<variable_specifications> 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<? extends ColumnSpecification> 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<variable_specifications> bound_names);
virtual ::shared_ptr<restrictions::restriction> new_IN_restriction(schema_ptr schema,
::shared_ptr<variable_specifications> 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<? extends ColumnSpecification> 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<ColumnSpecification> receivers = new ArrayList<>(2);
receivers.add(makeCollectionReceiver(receiver, true));
receivers.add(makeCollectionReceiver(receiver, false));
return receivers;
}
}
return Collections.singletonList(receiver);
}
std::vector<::shared_ptr<column_specification>> 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 <code>true</code> if the specified column is the last column of the partition key, <code>false</code>
* 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);
}
};
};

View File

@@ -39,9 +39,9 @@ namespace statements {
*/
class cf_statement : public parsed_statement {
protected:
std::experimental::optional<cf_name> _cf_name;
::shared_ptr<cf_name> _cf_name;
cf_statement(std::experimental::optional<cf_name>&& cf_name)
cf_statement(::shared_ptr<cf_name>&& cf_name)
: _cf_name(std::move(cf_name))
{ }

View File

@@ -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<attributes> attrs)
delete_statement(statement_type type, uint32_t bound_terms, schema_ptr s, std::unique_ptr<attributes> attrs)
: modification_statement{type, bound_terms, std::move(s), std::move(attrs)}
{ }

View File

@@ -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<std::experimental::optional<transport::messages::result_message>>
future<::shared_ptr<transport::messages::result_message>>
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<transport::messages::result_message>>(
std::experimental::optional<transport::messages::result_message>{});
return make_ready_future<::shared_ptr<transport::messages::result_message>>(
::shared_ptr<transport::messages::result_message>{});
});
}
@@ -310,7 +310,7 @@ modification_statement::execute_without_condition(service::storage_proxy& proxy,
});
}
future<std::experimental::optional<transport::messages::result_message>>
future<::shared_ptr<transport::messages::result_message>>
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<relation_ptr> 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_ptr<variable_spec
return stmt;
}
void
modification_statement::validate(const service::client_state& state) {
if (has_conditions() && attrs->is_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");
}
}
}
}

View File

@@ -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<attributes> attrs_)
modification_statement(statement_type type_, uint32_t bound_terms, schema_ptr schema_, std::unique_ptr<attributes> 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<operation> 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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
execute(service::storage_proxy& proxy, service::query_state& qs, const query_options& options) override;
virtual future<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
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<std::experimental::optional<transport::messages::result_message>>
future<::shared_ptr<transport::messages::result_message>>
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<cf_name> name, ::shared_ptr<attributes::raw> attrs, const conditions_vector& conditions, bool if_not_exists, bool if_exists)
parsed(::shared_ptr<cf_name> name, ::shared_ptr<attributes::raw> attrs, const conditions_vector& conditions, bool if_not_exists, bool if_exists)
: cf_statement{std::move(name)}
, _attrs{attrs}
, _conditions{conditions}

View File

@@ -45,17 +45,17 @@ private:
protected:
schema_altering_statement()
: cf_statement{std::experimental::optional<cf_name>{}}
: cf_statement{::shared_ptr<cf_name>{}}
, _is_column_family_level{false}
{ }
schema_altering_statement(std::experimental::optional<cf_name>&& name)
schema_altering_statement(::shared_ptr<cf_name>&& 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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
execute_internal(database& db, service::query_state& state, const query_options& options) override {
throw std::runtime_error("unsupported operation");
#if 0

View File

@@ -36,11 +36,11 @@ namespace statements {
class truncate_statement : public cf_statement, public virtual cql_statement, public ::enable_shared_from_this<truncate_statement> {
public:
truncate_statement(std::experimental::optional<cf_name>&& name)
truncate_statement(::shared_ptr<cf_name>&& 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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
execute_internal(database& db, service::query_state& state, const query_options& options) override {
throw std::runtime_error("unsupported operation");
}

View File

@@ -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<attributes> attrs)
update_statement(statement_type type, uint32_t bound_terms, schema_ptr s, std::unique_ptr<attributes> 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_identifier::raw>> _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<cf_name> name,
parsed_insert(::shared_ptr<cf_name> name,
::shared_ptr<attributes::raw> attrs,
std::vector<::shared_ptr<column_identifier::raw>> column_names,
std::vector<::shared_ptr<term::raw>> 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<modification_statement> 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<cf_name> name,
parsed_update(::shared_ptr<cf_name> name,
::shared_ptr<attributes::raw> attrs,
std::vector<std::pair<::shared_ptr<column_identifier::raw>, ::shared_ptr<operation::raw_update>>> updates,
std::vector<relation_ptr> where_clause,

View File

@@ -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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
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<std::experimental::optional<transport::messages::result_message>>
virtual future<::shared_ptr<transport::messages::result_message>>
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");

View File

@@ -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<bytes>;
/**
* 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 {

View File

@@ -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<bytes, tombstone> 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;
}

View File

@@ -48,10 +48,9 @@ timestamp_type constexpr max_timestamp = std::numeric_limits<timestamp_type>::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<bytes, tombstone> 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<bytes, tombstone> 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<sstring, column_family> column_families;
static future<keyspace> 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<sstring, keyspace> keyspaces;
static future<database> populate(sstring datadir);
keyspace* find_keyspace(sstring name);
keyspace* find_keyspace(const sstring& name);
};

150
enum_set.hh Normal file
View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2015 Cloudius Systems, Ltd.
*/
#pragma once
#include <cstddef>
#include <type_traits>
#include <limits>
/**
*
* 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<x, x::A, x::B, x::C>;
* using my_enumset = enum_set<my_enum>;
*
* static_assert(my_enumset::frozen<x::A, x::B>::contains<x::A>(), "it should...");
*
* assert(my_enumset::frozen<x::A, x::B>::contains(my_enumset::prepare<x::A>()));
*
* assert(my_enumset::frozen<x::A, x::B>::contains(x::A));
*
*/
template<typename EnumType, EnumType... Items>
struct super_enum {
using enum_type = EnumType;
template<enum_type... values>
struct max {
static constexpr enum_type max_of(enum_type a, enum_type b) {
return a > b ? a : b;
}
template<enum_type first, enum_type second, enum_type... rest>
static constexpr enum_type get() {
return max_of(first, get<second, rest...>());
}
template<enum_type first>
static constexpr enum_type get() { return first; }
static constexpr enum_type value = get<values...>();
};
template<enum_type... values>
struct min {
static constexpr enum_type min_of(enum_type a, enum_type b) {
return a < b ? a : b;
}
template<enum_type first, enum_type second, enum_type... rest>
static constexpr enum_type get() {
return min_of(first, get<second, rest...>());
}
template<enum_type first>
static constexpr enum_type get() { return first; }
static constexpr enum_type value = get<values...>();
};
using sequence_type = typename std::underlying_type<enum_type>::type;
template<enum_type Elem>
static constexpr sequence_type sequence_for() {
return static_cast<sequence_type>(Elem);
}
static sequence_type sequence_for(enum_type elem) {
return static_cast<sequence_type>(elem);
}
static constexpr sequence_type max_sequence = sequence_for<max<Items...>::value>();
static constexpr sequence_type min_sequence = sequence_for<min<Items...>::value>();
static_assert(min_sequence >= 0, "negative enum values unsupported");
};
template<typename Enum>
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<enum_type Elem>
static constexpr mask_type mask_for() {
// FIXME: for some reason Enum::sequence_for<Elem>() does not compile
return mask_type(1) << static_cast<typename Enum::sequence_type>(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<enum_type e>
static constexpr prepared prepare() {
return {mask_for<e>()};
}
static_assert(std::numeric_limits<mask_type>::max() >= ((size_t)1 << Enum::max_sequence), "mask type too small");
template<enum_type... items>
struct frozen {
template<enum_type first>
static constexpr mask_type make_mask() {
return mask_for<first>();
}
static constexpr mask_type make_mask() {
return 0;
}
template<enum_type first, enum_type second, enum_type... rest>
static constexpr mask_type make_mask() {
return mask_for<first>() | make_mask<second, rest...>();
}
static constexpr mask_type mask = make_mask<items...>();
template<enum_type Elem>
static constexpr bool contains() {
return mask & mask_for<Elem>();
}
static bool contains(enum_type e) {
return mask & mask_for(e);
}
static bool contains(prepared e) {
return mask & e.mask;
}
};
};

View File

@@ -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();
}

View File

@@ -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 <stdexcept>
#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

View File

@@ -26,7 +26,9 @@ public:
::shared_ptr<cql3::column_specification> 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<schema>;

View File

@@ -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");
}

View File

@@ -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
};
}
}

View File

@@ -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();
}
};
}

View File

@@ -12,6 +12,7 @@ all_tests = [
'output_stream_test',
'urchin/types_test',
'urchin/mutation_test',
'urchin/cql_query_test',
]
last_len = 0

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2015 Cloudius Systems
*/
#include <boost/test/included/unit_test.hpp>
#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<boost::any> pk, std::vector<boost::any> 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<const atomic_cell&>(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<database>();
// CQL: create table cf (p1 varchar, c1 int, r1 int, PRIMARY KEY (p1, c1));
keyspace ks;
auto cf_schema = make_lw_shared<schema>(ks_name, table_name,
std::vector<schema::column>({{"p1", utf8_type}}),
std::vector<schema::column>({{"c1", int32_type}}),
std::vector<schema::column>({{"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<conversation_state>(*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);
});
}

View File

@@ -4,6 +4,10 @@
#pragma once
#include "core/sstring.hh"
#include <vector>
#include <sstream>
/**
* Converts a vector of pointers to Printable elements.
* Printable is an object which has to_string() method.

View File

@@ -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 {
};
}

View File

@@ -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<const ANTLR_UINT8*>(query.begin()), ANTLR_ENC_UTF8, static_cast<ANTLR_UINT32>(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<const ANTLR_UINT8*>(query.begin()), ANTLR_ENC_UTF8, static_cast<ANTLR_UINT32>(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<>();

View File

@@ -59,6 +59,16 @@ public:
bytes serialize_value(const value_type& values) {
return ::serialize_value(*this, values);
}
bytes serialize_value_deep(const std::vector<boost::any>& values) {
// TODO: Optimize
std::vector<bytes_opt> 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<int32_t>::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<cql3::cql3_type> as_cql3_type() override {
assert(0);
}
};
using tuple_prefix = tuple_type<true>;

View File

@@ -3,6 +3,7 @@
*/
#include <boost/lexical_cast.hpp>
#include "cql3/cql3_type.hh"
#include "types.hh"
template <typename T, typename Compare>
@@ -66,14 +67,14 @@ struct int32_type_impl : simple_type_impl<int32_t> {
*reinterpret_cast<int32_t*>(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<int32_t>(s);
return boost::lexical_cast<int32_t>(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<int32_t> {
}
return to_sstring(compose_value(b));
}
virtual ::shared_ptr<cql3::cql3_type> as_cql3_type() override {
return cql3::native_cql3_type::int_;
}
};
struct long_type_impl : simple_type_impl<int64_t> {
@@ -103,16 +107,20 @@ struct long_type_impl : simple_type_impl<int64_t> {
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<cql3::cql3_type> 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::cql3_type> 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<const sstring&>(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<bytes>()(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<cql3::cql3_type> as_cql3_type() override {
return _cql3_type;
}
shared_ptr<cql3::cql3_type> _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<const bytes&>(value);
@@ -166,12 +178,15 @@ struct bytes_type_impl : public abstract_type {
virtual size_t hash(const bytes& v) override {
return std::hash<bytes>()(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<cql3::cql3_type> as_cql3_type() override {
return cql3::native_cql3_type::blob;
}
};
struct boolean_type_impl : public simple_type_impl<bool> {
@@ -189,12 +204,15 @@ struct boolean_type_impl : public simple_type_impl<bool> {
}
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<cql3::cql3_type> 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<bytes>()(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<cql3::cql3_type> 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<bytes>()(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<cql3::cql3_type> 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<db_clock::time_point> {
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<cql3::cql3_type> 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<bytes>()(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<cql3::cql3_type> as_cql3_type() override {
return cql3::native_cql3_type::uuid;
}
};
thread_local shared_ptr<abstract_type> int32_type(make_shared<int32_type_impl>());
thread_local shared_ptr<abstract_type> long_type(make_shared<long_type_impl>());
thread_local shared_ptr<abstract_type> ascii_type(make_shared<string_type_impl>("ascii"));
thread_local shared_ptr<abstract_type> ascii_type(make_shared<string_type_impl>("ascii", cql3::native_cql3_type::ascii));
thread_local shared_ptr<abstract_type> bytes_type(make_shared<bytes_type_impl>());
thread_local shared_ptr<abstract_type> utf8_type(make_shared<string_type_impl>("utf8"));
thread_local shared_ptr<abstract_type> utf8_type(make_shared<string_type_impl>("utf8", cql3::native_cql3_type::text));
thread_local shared_ptr<abstract_type> boolean_type(make_shared<boolean_type_impl>());
thread_local shared_ptr<abstract_type> date_type(make_shared<date_type_impl>());
thread_local shared_ptr<abstract_type> timeuuid_type(make_shared<timeuuid_type_impl>());

View File

@@ -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<char, uint32_t, 31>;
using bytes_view = std::experimental::string_view;
using bytes_opt = std::experimental::optional<bytes>;
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<cql3::cql3_type> as_cql3_type() = 0;
protected:
template <typename T, typename Compare = std::less<T>>
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<const char*>(x.data()), x.size());
}
inline
bytes
to_bytes(sstring_view x) {
return bytes(x.begin(), x.size());
}
inline
bytes
to_bytes(const sstring& x) {

View File

@@ -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() {}
}