Merge tag 'avi/user_type/v2' from seastar-dev.git

This commit is contained in:
Tomasz Grabiec
2015-04-20 16:00:21 +02:00
57 changed files with 722 additions and 584 deletions

View File

@@ -41,6 +41,7 @@ options {
#include "cql3/statements/batch_statement.hh"
#include "cql3/statements/ks_prop_defs.hh"
#include "cql3/selection/raw_selector.hh"
#include "cql3/selection/selectable_with_field_selection.hh"
#include "cql3/constants.hh"
#include "cql3/operation_impl.hh"
#include "cql3/error_listener.hh"
@@ -52,6 +53,7 @@ options {
#include "cql3/lists.hh"
#include "cql3/type_cast.hh"
#include "cql3/tuples.hh"
#include "cql3/user_types.hh"
#include "cql3/functions/function_name.hh"
#include "cql3/functions/function_call.hh"
#include "core/sstring.hh"
@@ -311,9 +313,7 @@ unaliasedSelector returns [shared_ptr<selectable::raw> s]
| K_TTL '(' c=cident ')' { tmp = make_shared<selectable::writetime_or_ttl::raw>(c, false); }
| f=functionName args=selectionFunctionArgs { tmp = ::make_shared<selectable::with_function::raw>(std::move(f), std::move(args)); }
)
#if 0
( '.' fi=cident { tmp = new Selectable.WithFieldSelection.Raw(tmp, fi); } )*
#endif
( '.' fi=cident { tmp = make_shared<selectable::with_field_selection::raw>(std::move(tmp), std::move(fi)); } )*
{ $s = tmp; }
;
@@ -1033,15 +1033,12 @@ collectionLiteral returns [shared_ptr<cql3::term::raw> value]
| '{' '}' { $value = make_shared(cql3::sets::literal({})); }
;
#if 0
usertypeLiteral returns [UserTypes.Literal ut]
@init{ Map<ColumnIdentifier, Term.Raw> m = new HashMap<ColumnIdentifier, Term.Raw>(); }
@after{ $ut = new UserTypes.Literal(m); }
usertypeLiteral returns [shared_ptr<cql3::user_types::literal> ut]
@init{ cql3::user_types::literal::elements_map_type m; }
@after{ $ut = ::make_shared<cql3::user_types::literal>(std::move(m)); }
// We don't allow empty literals because that conflicts with sets/maps and is currently useless since we don't allow empty user types
: '{' k1=ident ':' v1=term { m.put(k1, v1); } ( ',' kn=ident ':' vn=term { m.put(kn, vn); } )* '}'
: '{' k1=ident ':' v1=term { m.emplace(std::move(*k1), std::move(v1)); } ( ',' kn=ident ':' vn=term { m.emplace(std::move(*kn), std::move(vn)); } )* '}'
;
#endif
tupleLiteral returns [shared_ptr<cql3::tuples::literal> tt]
@init{ std::vector<shared_ptr<cql3::term::raw>> l; }
@@ -1052,9 +1049,7 @@ tupleLiteral returns [shared_ptr<cql3::tuples::literal> tt]
value returns [::shared_ptr<cql3::term::raw> value]
: c=constant { $value = c; }
| l=collectionLiteral { $value = l; }
#if 0
| u=usertypeLiteral { $value = u; }
#endif
| t=tupleLiteral { $value = t; }
| K_NULL { $value = cql3::constants::NULL_LITERAL; }
| ':' id=ident { $value = new_bind_variables(id); }

View File

@@ -1,201 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
import java.util.*;
import org.apache.cassandra.db.marshal.CollectionType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.Server;
/**
* Static helper methods and classes for user types.
*/
public abstract class UserTypes
{
private UserTypes() {}
public static ColumnSpecification fieldSpecOf(ColumnSpecification column, int field)
{
UserType ut = (UserType)column.type;
return new ColumnSpecification(column.ksName,
column.cfName,
new ColumnIdentifier(column.name + "." + UTF8Type.instance.compose(ut.fieldName(field)), true),
ut.fieldType(field));
}
public static class Literal implements Term.Raw
{
public final Map<ColumnIdentifier, Term.Raw> entries;
public Literal(Map<ColumnIdentifier, Term.Raw> entries)
{
this.entries = entries;
}
public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
{
validateAssignableTo(keyspace, receiver);
UserType ut = (UserType)receiver.type;
boolean allTerminal = true;
List<Term> values = new ArrayList<>(entries.size());
int foundValues = 0;
for (int i = 0; i < ut.size(); i++)
{
ColumnIdentifier field = new ColumnIdentifier(ut.fieldName(i), UTF8Type.instance);
Term.Raw raw = entries.get(field);
if (raw == null)
raw = Constants.NULL_LITERAL;
else
++foundValues;
Term value = raw.prepare(keyspace, fieldSpecOf(receiver, i));
if (value instanceof Term.NonTerminal)
allTerminal = false;
values.add(value);
}
if (foundValues != entries.size())
{
// We had some field that are not part of the type
for (ColumnIdentifier id : entries.keySet())
if (!ut.fieldNames().contains(id.bytes))
throw new InvalidRequestException(String.format("Unknown field '%s' in value of user defined type %s", id, ut.getNameAsString()));
}
DelayedValue value = new DelayedValue(((UserType)receiver.type), values);
return allTerminal ? value.bind(QueryOptions.DEFAULT) : value;
}
private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
{
if (!(receiver.type instanceof UserType))
throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver, receiver.type.asCQL3Type()));
UserType ut = (UserType)receiver.type;
for (int i = 0; i < ut.size(); i++)
{
ColumnIdentifier field = new ColumnIdentifier(ut.fieldName(i), UTF8Type.instance);
Term.Raw value = entries.get(field);
if (value == null)
continue;
ColumnSpecification fieldSpec = fieldSpecOf(receiver, i);
if (!value.testAssignment(keyspace, fieldSpec).isAssignable())
throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver, field, fieldSpec.type.asCQL3Type()));
}
}
public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver)
{
try
{
validateAssignableTo(keyspace, receiver);
return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE;
}
catch (InvalidRequestException e)
{
return AssignmentTestable.TestResult.NOT_ASSIGNABLE;
}
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("{");
Iterator<Map.Entry<ColumnIdentifier, Term.Raw>> iter = entries.entrySet().iterator();
while (iter.hasNext())
{
Map.Entry<ColumnIdentifier, Term.Raw> entry = iter.next();
sb.append(entry.getKey()).append(":").append(entry.getValue());
if (iter.hasNext())
sb.append(", ");
}
sb.append("}");
return sb.toString();
}
}
// Same purpose than Lists.DelayedValue, except we do handle bind marker in that case
public static class DelayedValue extends Term.NonTerminal
{
private final UserType type;
private final List<Term> values;
public DelayedValue(UserType type, List<Term> values)
{
this.type = type;
this.values = values;
}
public boolean usesFunction(String ksName, String functionName)
{
if (values != null)
for (Term value : values)
if (value != null && value.usesFunction(ksName, functionName))
return true;
return false;
}
public boolean containsBindMarker()
{
for (Term t : values)
if (t.containsBindMarker())
return true;
return false;
}
public void collectMarkerSpecification(VariableSpecifications boundNames)
{
for (int i = 0; i < type.size(); i++)
values.get(i).collectMarkerSpecification(boundNames);
}
private ByteBuffer[] bindInternal(QueryOptions options) throws InvalidRequestException
{
int version = options.getProtocolVersion();
ByteBuffer[] buffers = new ByteBuffer[values.size()];
for (int i = 0; i < type.size(); i++)
{
buffers[i] = values.get(i).bindAndGet(options);
// Inside UDT values, we must force the serialization of collections to v3 whatever protocol
// version is in use since we're going to store directly that serialized value.
if (version < Server.VERSION_3 && type.fieldType(i).isCollection() && buffers[i] != null)
buffers[i] = ((CollectionType)type.fieldType(i)).getSerializer().reserializeToV3(buffers[i]);
}
return buffers;
}
public Constants.Value bind(QueryOptions options) throws InvalidRequestException
{
return new Constants.Value(bindAndGet(options));
}
@Override
public ByteBuffer bindAndGet(QueryOptions options) throws InvalidRequestException
{
return UserType.buildValue(bindInternal(options));
}
}
}

View File

@@ -31,7 +31,7 @@
namespace cql3 {
::shared_ptr<term> abstract_marker::raw::prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver)
::shared_ptr<term> abstract_marker::raw::prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver)
{
auto receiver_type = ::dynamic_pointer_cast<collection_type_impl>(receiver->type);
if (receiver_type == nullptr) {
@@ -47,7 +47,7 @@ namespace cql3 {
assert(0);
}
::shared_ptr<term> abstract_marker::in_raw::prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) {
::shared_ptr<term> abstract_marker::in_raw::prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) {
return ::make_shared<lists::marker>(_bind_index, make_in_receiver(receiver));
}

View File

@@ -65,9 +65,9 @@ public:
: _bind_index{bind_index}
{ }
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
virtual assignment_testable::test_result test_assignment(const sstring& keyspace, ::shared_ptr<column_specification> receiver) override {
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override {
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
}
@@ -95,7 +95,7 @@ public:
}
public:
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
};
};

View File

@@ -29,6 +29,8 @@
#include <memory>
#include <vector>
class database;
namespace cql3 {
class assignment_testable {
@@ -52,7 +54,7 @@ public:
// Test all elements of toTest for assignment. If all are exact match, return exact match. If any is not assignable,
// return not assignable. Otherwise, return weakly assignable.
template <typename AssignmentTestablePtrRange>
static test_result test_all(const sstring& keyspace, ::shared_ptr<column_specification> receiver,
static test_result test_all(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver,
AssignmentTestablePtrRange&& to_test) {
test_result res = test_result::EXACT_MATCH;
for (auto&& rt : to_test) {
@@ -61,7 +63,7 @@ public:
continue;
}
test_result t = rt->test_assignment(keyspace, receiver);
test_result t = rt->test_assignment(db, keyspace, receiver);
if (t == test_result::NOT_ASSIGNABLE) {
return test_result::NOT_ASSIGNABLE;
}
@@ -81,7 +83,7 @@ public:
* Most caller should just call the isAssignable() method on the result, though functions have a use for
* testing "strong" equality to decide the most precise overload to pick when multiple could match.
*/
virtual test_result test_assignment(const sstring& keyspace, ::shared_ptr<column_specification> receiver) = 0;
virtual test_result test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) = 0;
// for error reporting
virtual sstring assignment_testable_source_context() const = 0;

View File

@@ -126,9 +126,9 @@ public:
::shared_ptr<term::raw> timestamp;
::shared_ptr<term::raw> time_to_live;
std::unique_ptr<attributes> prepare(const sstring& ks_name, const sstring& cf_name) {
auto ts = !timestamp ? ::shared_ptr<term>{} : timestamp->prepare(ks_name, timestamp_receiver(ks_name, cf_name));
auto ttl = !time_to_live ? ::shared_ptr<term>{} : time_to_live->prepare(ks_name, time_to_live_receiver(ks_name, cf_name));
std::unique_ptr<attributes> prepare(database& db, const sstring& ks_name, const sstring& cf_name) {
auto ts = !timestamp ? ::shared_ptr<term>{} : timestamp->prepare(db, ks_name, timestamp_receiver(ks_name, cf_name));
auto ttl = !time_to_live ? ::shared_ptr<term>{} : time_to_live->prepare(db, ks_name, time_to_live_receiver(ks_name, cf_name));
return std::unique_ptr<attributes>{new attributes{std::move(ts), std::move(ttl)}};
}

View File

@@ -58,7 +58,7 @@ void column_condition::collect_marker_specificaton(::shared_ptr<variable_specifi
}
::shared_ptr<column_condition>
column_condition::raw::prepare(const sstring& keyspace, const column_definition& receiver) {
column_condition::raw::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
if (receiver.type->is_counter()) {
throw exceptions::invalid_request_exception("Conditions on counters are not supported");
}
@@ -66,16 +66,16 @@ column_condition::raw::prepare(const sstring& keyspace, const column_definition&
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));
return column_condition::in_condition(receiver, _in_marker->prepare(db, keyspace, receiver.column_specification));
}
std::vector<::shared_ptr<term>> terms;
for (auto&& value : _in_values) {
terms.push_back(value->prepare(keyspace, receiver.column_specification));
terms.push_back(value->prepare(db, 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);
return column_condition::condition(receiver, _value->prepare(db, keyspace, receiver.column_specification), _op);
}
}

View File

@@ -758,7 +758,7 @@ public:
std::move(collection_element), operator_type::IN);
}
::shared_ptr<column_condition> prepare(const sstring& keyspace, const column_definition& receiver);
::shared_ptr<column_condition> prepare(database& db, const sstring& keyspace, const column_definition& receiver);
};
};

View File

@@ -31,7 +31,7 @@ std::ostream& operator<<(std::ostream& out, const column_identifier::raw& id) {
}
::shared_ptr<selection::selector::factory>
column_identifier::new_selector_factory(schema_ptr schema, std::vector<const column_definition*>& defs) {
column_identifier::new_selector_factory(database& db, schema_ptr schema, std::vector<const column_definition*>& defs) {
auto def = get_column_definition(schema, *this);
if (!def) {
throw exceptions::invalid_request_exception(sprint("Undefined name %s in selection clause", _text));

View File

@@ -127,7 +127,7 @@ public:
}
#endif
virtual ::shared_ptr<selection::selector::factory> new_selector_factory(schema_ptr schema,
virtual ::shared_ptr<selection::selector::factory> new_selector_factory(database& db, schema_ptr schema,
std::vector<const column_definition*>& defs) override;
/**

View File

@@ -63,7 +63,7 @@ constants::literal::parsed_value(::shared_ptr<abstract_type> validator)
}
assignment_testable::test_result
constants::literal::test_assignment(const sstring& keyspace, ::shared_ptr<column_specification> receiver)
constants::literal::test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver)
{
auto receiver_type = receiver->type->as_cql3_type();
if (receiver_type->is_collection()) {
@@ -127,9 +127,9 @@ constants::literal::test_assignment(const sstring& keyspace, ::shared_ptr<column
}
::shared_ptr<term>
constants::literal::prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver)
constants::literal::prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver)
{
if (!is_assignable(test_assignment(keyspace, receiver))) {
if (!is_assignable(test_assignment(db, 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()));
}

View File

@@ -90,14 +90,15 @@ public:
};
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))) {
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override {
if (!is_assignable(test_assignment(db, keyspace, receiver))) {
throw exceptions::invalid_request_exception("Invalid null value for counter increment/decrement");
}
return NULL_VALUE;
}
virtual assignment_testable::test_result test_assignment(const sstring& keyspace,
virtual assignment_testable::test_result test_assignment(database& db,
const sstring& keyspace,
::shared_ptr<column_specification> receiver) override {
return receiver->type->is_counter()
? assignment_testable::test_result::NOT_ASSIGNABLE
@@ -145,7 +146,7 @@ public:
return ::make_shared<literal>(type::HEX, text);
}
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver);
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver);
private:
bytes parsed_value(::shared_ptr<abstract_type> validator);
public:
@@ -153,7 +154,7 @@ public:
return _text;
}
virtual assignment_testable::test_result test_assignment(const sstring& keyspace, ::shared_ptr<column_specification> receiver);
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver);
virtual sstring to_string() const override {
return _type == type::STRING ? sstring(sprint("'%s'", _text)) : _text;

View File

@@ -14,7 +14,7 @@ public:
: _type{type}
{ }
public:
virtual shared_ptr<cql3_type> prepare(const sstring& keyspace) {
virtual shared_ptr<cql3_type> prepare(database& db, const sstring& keyspace) {
return _type;
}
@@ -61,7 +61,7 @@ public:
return true;
}
virtual shared_ptr<cql3_type> prepare(const sstring& keyspace) override {
virtual shared_ptr<cql3_type> prepare(database& db, const sstring& keyspace) override {
assert(_values); // "Got null values type for a collection";
if (!_frozen && _values->supports_freezing() && !_values->_frozen) {
@@ -78,12 +78,12 @@ public:
}
if (_kind == &collection_type_impl::kind::list) {
return make_shared(cql3_type(to_string(), list_type_impl::get_instance(_values->prepare(keyspace)->get_type(), !_frozen), false));
return make_shared(cql3_type(to_string(), list_type_impl::get_instance(_values->prepare(db, keyspace)->get_type(), !_frozen), false));
} else if (_kind == &collection_type_impl::kind::set) {
return make_shared(cql3_type(to_string(), set_type_impl::get_instance(_values->prepare(keyspace)->get_type(), !_frozen), false));
return make_shared(cql3_type(to_string(), set_type_impl::get_instance(_values->prepare(db, keyspace)->get_type(), !_frozen), false));
} else if (_kind == &collection_type_impl::kind::map) {
assert(_keys); // "Got null keys type for a collection";
return make_shared(cql3_type(to_string(), map_type_impl::get_instance(_keys->prepare(keyspace)->get_type(), _values->prepare(keyspace)->get_type(), !_frozen), false));
return make_shared(cql3_type(to_string(), map_type_impl::get_instance(_keys->prepare(db, keyspace)->get_type(), _values->prepare(db, keyspace)->get_type(), !_frozen), false));
}
abort();
}
@@ -122,7 +122,7 @@ public:
}
_frozen = true;
}
virtual shared_ptr<cql3_type> prepare(const sstring& keyspace) override {
virtual shared_ptr<cql3_type> prepare(database& db, const sstring& keyspace) override {
if (!_frozen) {
freeze();
}
@@ -131,7 +131,7 @@ public:
if (t->is_counter()) {
throw exceptions::invalid_request_exception("Counters are not allowed inside tuples");
}
ts.push_back(t->prepare(keyspace)->get_type());
ts.push_back(t->prepare(db, keyspace)->get_type());
}
return make_cql3_tuple_type(tuple_type_impl::get_instance(std::move(ts)));
}

View File

@@ -29,6 +29,8 @@
#include <iosfwd>
#include "enum_set.hh"
class database;
namespace cql3 {
class cql3_type final {
@@ -53,7 +55,7 @@ public:
virtual bool is_counter() const;
virtual std::experimental::optional<sstring> keyspace() const;
virtual void freeze();
virtual shared_ptr<cql3_type> prepare(const sstring& keyspace) = 0;
virtual shared_ptr<cql3_type> prepare(database& db, const sstring& keyspace) = 0;
static shared_ptr<raw> from(shared_ptr<cql3_type> type);
#if 0
public static Raw userType(UTName name)

View File

@@ -57,12 +57,12 @@ public:
raw(function_name name, std::vector<shared_ptr<term::raw>> terms)
: _name(std::move(name)), _terms(std::move(terms)) {
}
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
private:
// All parameters must be terminal
static bytes_opt execute(scalar_function& fun, std::vector<shared_ptr<term>> parameters);
public:
virtual assignment_testable::test_result test_assignment(const sstring& keyspace, shared_ptr<column_specification> receiver) override;
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) override;
virtual sstring to_string() const override;
};
};

View File

@@ -92,7 +92,8 @@ functions::get_overload_count(const function_name& name) {
}
shared_ptr<function>
functions::get(const sstring& keyspace,
functions::get(database& db,
const sstring& keyspace,
const function_name& name,
const std::vector<shared_ptr<assignment_testable>>& provided_args,
const sstring& receiver_ks,
@@ -128,13 +129,13 @@ functions::get(const sstring& keyspace,
// Fast path if there is only one choice
if (candidates.size() == 1) {
auto fun = std::move(candidates[0]);
validate_types(keyspace, fun, provided_args, receiver_ks, receiver_cf);
validate_types(db, keyspace, fun, provided_args, receiver_ks, receiver_cf);
return fun;
}
std::vector<shared_ptr<function>> compatibles;
for (auto&& to_test : candidates) {
auto r = match_arguments(keyspace, to_test, provided_args, receiver_ks, receiver_cf);
auto r = match_arguments(db, keyspace, to_test, provided_args, receiver_ks, receiver_cf);
switch (r) {
case assignment_testable::test_result::EXACT_MATCH:
// We always favor exact matches
@@ -186,7 +187,8 @@ functions::find(const function_name& name, const std::vector<data_type>& arg_typ
// This method and matchArguments are somewhat duplicate, but this method allows us to provide more precise errors in the common
// case where there is no override for a given function. This is thus probably worth the minor code duplication.
void
functions::validate_types(const sstring& keyspace,
functions::validate_types(database& db,
const sstring& keyspace,
shared_ptr<function> fun,
const std::vector<shared_ptr<assignment_testable>>& provided_args,
const sstring& receiver_ks,
@@ -207,7 +209,7 @@ functions::validate_types(const sstring& keyspace,
}
auto&& expected = make_arg_spec(receiver_ks, receiver_cf, *fun, i);
if (!is_assignable(provided->test_assignment(keyspace, expected))) {
if (!is_assignable(provided->test_assignment(db, keyspace, expected))) {
throw exceptions::invalid_request_exception(
sprint("Type error: %s cannot be passed as argument %d of function %s of type %s",
provided, i, fun->name(), expected->type->as_cql3_type()));
@@ -216,7 +218,7 @@ functions::validate_types(const sstring& keyspace,
}
assignment_testable::test_result
functions::match_arguments(const sstring& keyspace,
functions::match_arguments(database& db, const sstring& keyspace,
shared_ptr<function> fun,
const std::vector<shared_ptr<assignment_testable>>& provided_args,
const sstring& receiver_ks,
@@ -234,7 +236,7 @@ functions::match_arguments(const sstring& keyspace,
continue;
}
auto&& expected = make_arg_spec(receiver_ks, receiver_cf, *fun, i);
auto arg_res = provided->test_assignment(keyspace, expected);
auto arg_res = provided->test_assignment(db, keyspace, expected);
if (arg_res == assignment_testable::test_result::NOT_ASSIGNABLE) {
return assignment_testable::test_result::NOT_ASSIGNABLE;
}
@@ -340,14 +342,14 @@ function_call::make_terminal(shared_ptr<function> fun, bytes_opt result, seriali
}
::shared_ptr<term>
function_call::raw::prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) {
function_call::raw::prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) {
std::vector<shared_ptr<assignment_testable>> args;
args.reserve(_terms.size());
std::transform(_terms.begin(), _terms.end(), std::back_inserter(args),
[] (auto&& x) -> shared_ptr<assignment_testable> {
return x;
});
auto&& fun = functions::functions::get(keyspace, _name, args, receiver->ks_name, receiver->cf_name);
auto&& fun = functions::functions::get(db, keyspace, _name, args, receiver->ks_name, receiver->cf_name);
if (!fun) {
throw exceptions::invalid_request_exception(sprint("Unknown function %s called", _name));
}
@@ -375,7 +377,7 @@ function_call::raw::prepare(const sstring& keyspace, ::shared_ptr<column_specifi
parameters.reserve(_terms.size());
bool all_terminal = true;
for (size_t i = 0; i < _terms.size(); ++i) {
auto&& t = _terms[i]->prepare(keyspace, functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i));
auto&& t = _terms[i]->prepare(db, keyspace, functions::make_arg_spec(receiver->ks_name, receiver->cf_name, *scalar_fun, i));
if (dynamic_cast<non_terminal*>(t.get())) {
all_terminal = false;
}
@@ -405,13 +407,13 @@ function_call::raw::execute(scalar_function& fun, std::vector<shared_ptr<term>>
}
assignment_testable::test_result
function_call::raw::test_assignment(const sstring& keyspace, shared_ptr<column_specification> receiver) {
function_call::raw::test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
// Note: Functions.get() will return null if the function doesn't exist, or throw is no function matching
// the arguments can be found. We may get one of those if an undefined/wrong function is used as argument
// of another, existing, function. In that case, we return true here because we'll throw a proper exception
// later with a more helpful error message that if we were to return false here.
try {
auto&& fun = functions::get(keyspace, _name, _terms, receiver->ks_name, receiver->cf_name);
auto&& fun = functions::get(db, keyspace, _name, _terms, receiver->ks_name, receiver->cf_name);
if (fun && receiver->type->equals(fun->return_type())) {
return assignment_testable::test_result::EXACT_MATCH;
} else if (!fun || receiver->type->is_value_compatible_with(*fun->return_type())) {

View File

@@ -58,31 +58,34 @@ public:
const function& fun, size_t i);
static int get_overload_count(const function_name& name);
public:
static shared_ptr<function> get(const sstring& keyspace,
static shared_ptr<function> get(database& db,
const sstring& keyspace,
const function_name& name,
const std::vector<shared_ptr<assignment_testable>>& provided_args,
const sstring& receiver_ks,
const sstring& receiver_cf);
template <typename AssignmentTestablePtrRange>
static shared_ptr<function> get(const sstring& keyspace,
static shared_ptr<function> get(database& db,
const sstring& keyspace,
const function_name& name,
AssignmentTestablePtrRange&& provided_args,
const sstring& receiver_ks,
const sstring& receiver_cf) {
const std::vector<shared_ptr<assignment_testable>> args(std::begin(provided_args), std::end(provided_args));
return get(keyspace, name, args, receiver_ks, receiver_cf);
return get(db, keyspace, name, args, receiver_ks, receiver_cf);
}
static std::vector<shared_ptr<function>> find(const function_name& name);
static shared_ptr<function> find(const function_name& name, const std::vector<data_type>& arg_types);
private:
// This method and matchArguments are somewhat duplicate, but this method allows us to provide more precise errors in the common
// case where there is no override for a given function. This is thus probably worth the minor code duplication.
static void validate_types(const sstring& keyspace,
static void validate_types(database& db,
const sstring& keyspace,
shared_ptr<function> fun,
const std::vector<shared_ptr<assignment_testable>>& provided_args,
const sstring& receiver_ks,
const sstring& receiver_cf);
static assignment_testable::test_result match_arguments(const sstring& keyspace,
static assignment_testable::test_result match_arguments(database& db, const sstring& keyspace,
shared_ptr<function> fun,
const std::vector<shared_ptr<assignment_testable>>& provided_args,
const sstring& receiver_ks,

View File

@@ -25,15 +25,15 @@ lists::value_spec_of(shared_ptr<column_specification> column) {
}
shared_ptr<term>
lists::literal::prepare(const sstring& keyspace, shared_ptr<column_specification> receiver) {
validate_assignable_to(keyspace, receiver);
lists::literal::prepare(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
validate_assignable_to(db, keyspace, receiver);
auto&& value_spec = value_spec_of(receiver);
std::vector<shared_ptr<term>> values;
values.reserve(_elements.size());
bool all_terminal = true;
for (auto rt : _elements) {
auto&& t = rt->prepare(keyspace, value_spec);
auto&& t = rt->prepare(db, keyspace, value_spec);
if (t->contains_bind_marker()) {
throw exceptions::invalid_request_exception(sprint("Invalid list literal for %s: bind variables are not supported inside collection literals", *receiver->name));
@@ -52,14 +52,14 @@ lists::literal::prepare(const sstring& keyspace, shared_ptr<column_specification
}
void
lists::literal::validate_assignable_to(const sstring keyspace, shared_ptr<column_specification> receiver) {
lists::literal::validate_assignable_to(database& db, const sstring keyspace, shared_ptr<column_specification> receiver) {
if (!dynamic_pointer_cast<list_type_impl>(receiver->type)) {
throw exceptions::invalid_request_exception(sprint("Invalid list literal for %s of type %s",
*receiver->name, *receiver->type->as_cql3_type()));
}
auto&& value_spec = value_spec_of(receiver);
for (auto rt : _elements) {
if (!is_assignable(rt->test_assignment(keyspace, value_spec))) {
if (!is_assignable(rt->test_assignment(db, keyspace, value_spec))) {
throw exceptions::invalid_request_exception(sprint("Invalid list literal for %s: value %s is not of type %s",
*receiver->name, *rt, *value_spec->type->as_cql3_type()));
}
@@ -67,7 +67,7 @@ lists::literal::validate_assignable_to(const sstring keyspace, shared_ptr<column
}
assignment_testable::test_result
lists::literal::test_assignment(const sstring& keyspace, shared_ptr<column_specification> receiver) {
lists::literal::test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
if (!dynamic_pointer_cast<list_type_impl>(receiver->type)) {
return assignment_testable::test_result::NOT_ASSIGNABLE;
}
@@ -81,7 +81,7 @@ lists::literal::test_assignment(const sstring& keyspace, shared_ptr<column_speci
std::vector<shared_ptr<assignment_testable>> to_test;
to_test.reserve(_elements.size());
std::copy(_elements.begin(), _elements.end(), std::back_inserter(to_test));
return assignment_testable::test_all(keyspace, value_spec, to_test);
return assignment_testable::test_all(db, keyspace, value_spec, to_test);
}
sstring

View File

@@ -73,11 +73,11 @@ public:
explicit literal(std::vector<shared_ptr<term::raw>> elements)
: _elements(std::move(elements)) {
}
shared_ptr<term> prepare(const sstring& keyspace, shared_ptr<column_specification> receiver);
shared_ptr<term> prepare(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver);
private:
void validate_assignable_to(const sstring keyspace, shared_ptr<column_specification> receiver);
void validate_assignable_to(database& db, const sstring keyspace, shared_ptr<column_specification> receiver);
public:
virtual assignment_testable::test_result test_assignment(const sstring& keyspace, shared_ptr<column_specification> receiver) override;
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) override;
virtual sstring to_string() const override;
};

View File

@@ -48,8 +48,8 @@ maps::value_spec_of(column_specification& column) {
}
::shared_ptr<term>
maps::literal::prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) {
validate_assignable_to(keyspace, *receiver);
maps::literal::prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) {
validate_assignable_to(db, keyspace, *receiver);
auto key_spec = maps::key_spec_of(*receiver);
auto value_spec = maps::value_spec_of(*receiver);
@@ -57,8 +57,8 @@ maps::literal::prepare(const sstring& keyspace, ::shared_ptr<column_specificatio
values.reserve(entries.size());
bool all_terminal = true;
for (auto&& entry : entries) {
auto k = entry.first->prepare(keyspace, key_spec);
auto v = entry.second->prepare(keyspace, value_spec);
auto k = entry.first->prepare(db, keyspace, key_spec);
auto v = entry.second->prepare(db, keyspace, value_spec);
if (k->contains_bind_marker() || v->contains_bind_marker()) {
throw exceptions::invalid_request_exception(sprint("Invalid map literal for %s: bind variables are not supported inside collection literals", *receiver->name));
@@ -79,24 +79,24 @@ maps::literal::prepare(const sstring& keyspace, ::shared_ptr<column_specificatio
}
void
maps::literal::validate_assignable_to(const sstring& keyspace, column_specification& receiver) {
maps::literal::validate_assignable_to(database& db, const sstring& keyspace, column_specification& receiver) {
if (!dynamic_pointer_cast<map_type_impl>(receiver.type)) {
throw exceptions::invalid_request_exception(sprint("Invalid map literal for %s of type %s", *receiver.name, *receiver.type->as_cql3_type()));
}
auto&& key_spec = maps::key_spec_of(receiver);
auto&& value_spec = maps::value_spec_of(receiver);
for (auto&& entry : entries) {
if (!is_assignable(entry.first->test_assignment(keyspace, key_spec))) {
if (!is_assignable(entry.first->test_assignment(db, keyspace, key_spec))) {
throw exceptions::invalid_request_exception(sprint("Invalid map literal for %s: key %s is not of type %s", *receiver.name, *entry.first, *key_spec->type->as_cql3_type()));
}
if (!is_assignable(entry.second->test_assignment(keyspace, value_spec))) {
if (!is_assignable(entry.second->test_assignment(db, keyspace, value_spec))) {
throw exceptions::invalid_request_exception(sprint("Invalid map literal for %s: value %s is not of type %s", *receiver.name, *entry.second, *value_spec->type->as_cql3_type()));
}
}
}
assignment_testable::test_result
maps::literal::test_assignment(const sstring& keyspace, ::shared_ptr<column_specification> receiver) {
maps::literal::test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) {
throw std::runtime_error("not implemented");
#if 0
if (!(receiver.type instanceof MapType))

View File

@@ -50,11 +50,11 @@ public:
literal(const std::vector<std::pair<::shared_ptr<term::raw>, ::shared_ptr<term::raw>>>& entries_)
: entries{entries_}
{ }
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
private:
void validate_assignable_to(const sstring& keyspace, column_specification& receiver);
void validate_assignable_to(database& db, const sstring& keyspace, column_specification& receiver);
public:
virtual assignment_testable::test_result test_assignment(const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override;
virtual sstring to_string() const override;
};

View File

@@ -30,7 +30,7 @@ namespace cql3 {
shared_ptr<operation>
operation::set_element::prepare(const sstring& keyspace, const column_definition& receiver) {
operation::set_element::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
using exceptions::invalid_request_exception;
auto rtype = dynamic_pointer_cast<collection_type_impl>(receiver.type);
if (!rtype) {
@@ -40,14 +40,14 @@ operation::set_element::prepare(const sstring& keyspace, const column_definition
}
if (&rtype->_kind == &collection_type_impl::kind::list) {
auto&& idx = _selector->prepare(keyspace, lists::index_spec_of(receiver.column_specification));
auto&& lval = _value->prepare(keyspace, lists::value_spec_of(receiver.column_specification));
auto&& idx = _selector->prepare(db, keyspace, lists::index_spec_of(receiver.column_specification));
auto&& lval = _value->prepare(db, keyspace, lists::value_spec_of(receiver.column_specification));
return make_shared<lists::setter_by_index>(receiver, idx, lval);
} else if (&rtype->_kind == &collection_type_impl::kind::set) {
throw invalid_request_exception(sprint("Invalid operation (%s) for set column %s", receiver, receiver.name()));
} else if (&rtype->_kind == &collection_type_impl::kind::map) {
auto key = _selector->prepare(keyspace, maps::key_spec_of(*receiver.column_specification));
auto mval = _value->prepare(keyspace, maps::value_spec_of(*receiver.column_specification));
auto key = _selector->prepare(db, keyspace, maps::key_spec_of(*receiver.column_specification));
auto mval = _value->prepare(db, keyspace, maps::value_spec_of(*receiver.column_specification));
return make_shared<maps::setter_by_key>(receiver, key, mval);
}
abort();
@@ -61,8 +61,8 @@ operation::set_element::is_compatible_with(shared_ptr<raw_update> other) {
}
shared_ptr<operation>
operation::addition::prepare(const sstring& keyspace, const column_definition& receiver) {
auto v = _value->prepare(keyspace, receiver.column_specification);
operation::addition::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
auto v = _value->prepare(db, keyspace, receiver.column_specification);
auto ctype = dynamic_pointer_cast<collection_type_impl>(receiver.type);
if (!ctype) {
@@ -97,7 +97,7 @@ operation::addition::is_compatible_with(shared_ptr<raw_update> other) {
}
shared_ptr<operation>
operation::subtraction::prepare(const sstring& keyspace, const column_definition& receiver) {
operation::subtraction::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
auto ctype = dynamic_pointer_cast<collection_type_impl>(receiver.type);
if (!ctype) {
fail(unimplemented::cause::COUNTERS);
@@ -113,9 +113,9 @@ operation::subtraction::prepare(const sstring& keyspace, const column_definition
}
if (&ctype->_kind == &collection_type_impl::kind::list) {
return make_shared<lists::discarder>(receiver, _value->prepare(keyspace, receiver.column_specification));
return make_shared<lists::discarder>(receiver, _value->prepare(db, keyspace, receiver.column_specification));
} else if (&ctype->_kind == &collection_type_impl::kind::set) {
return make_shared<sets::discarder>(receiver, _value->prepare(keyspace, receiver.column_specification));
return make_shared<sets::discarder>(receiver, _value->prepare(db, keyspace, receiver.column_specification));
} else if (&ctype->_kind == &collection_type_impl::kind::map) {
auto&& mtype = dynamic_pointer_cast<map_type_impl>(ctype);
// The value for a map subtraction is actually a set
@@ -124,7 +124,7 @@ operation::subtraction::prepare(const sstring& keyspace, const column_definition
receiver.column_specification->cf_name,
receiver.column_specification->name,
set_type_impl::get_instance(mtype->get_keys_type(), false));
return make_shared<sets::discarder>(receiver, _value->prepare(keyspace, std::move(vr)));
return make_shared<sets::discarder>(receiver, _value->prepare(db, keyspace, std::move(vr)));
}
abort();
}
@@ -135,7 +135,7 @@ operation::subtraction::is_compatible_with(shared_ptr<raw_update> other) {
}
shared_ptr<operation>
operation::prepend::prepare(const sstring& keyspace, const column_definition& receiver) {
operation::prepend::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
warn(unimplemented::cause::COLLECTIONS);
throw exceptions::invalid_request_exception("unimplemented, go away");
// FIXME:
@@ -158,8 +158,8 @@ operation::prepend::is_compatible_with(shared_ptr<raw_update> other) {
::shared_ptr <operation>
operation::set_value::prepare(const sstring& keyspace, const column_definition& receiver) {
auto v = _value->prepare(keyspace, receiver.column_specification);
operation::set_value::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
auto v = _value->prepare(db, keyspace, receiver.column_specification);
if (receiver.type->is_counter()) {
throw exceptions::invalid_request_exception(sprint("Cannot set the value of counter column %s (counters can only be incremented/decremented, not set)", receiver.name_as_text()));
@@ -194,7 +194,7 @@ operation::element_deletion::affected_column() {
}
shared_ptr<operation>
operation::element_deletion::prepare(const sstring& keyspace, const column_definition& receiver) {
operation::element_deletion::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
if (!receiver.type->is_collection()) {
throw exceptions::invalid_request_exception(sprint("Invalid deletion operation for non collection column %s", receiver.name()));
} else if (!receiver.type->is_multi_cell()) {
@@ -202,13 +202,13 @@ operation::element_deletion::prepare(const sstring& keyspace, const column_defin
}
auto ctype = static_pointer_cast<collection_type_impl>(receiver.type);
if (&ctype->_kind == &collection_type_impl::kind::list) {
auto&& idx = _element->prepare(keyspace, lists::index_spec_of(receiver.column_specification));
auto&& idx = _element->prepare(db, keyspace, lists::index_spec_of(receiver.column_specification));
return make_shared<lists::discarder_by_index>(receiver, std::move(idx));
} else if (&ctype->_kind == &collection_type_impl::kind::set) {
auto&& elt = _element->prepare(keyspace, sets::value_spec_of(receiver.column_specification));
auto&& elt = _element->prepare(db, keyspace, sets::value_spec_of(receiver.column_specification));
return make_shared<sets::discarder>(receiver, std::move(elt));
} else if (&ctype->_kind == &collection_type_impl::kind::map) {
auto&& key = _element->prepare(keyspace, maps::key_spec_of(*receiver.column_specification));
auto&& key = _element->prepare(db, keyspace, maps::key_spec_of(*receiver.column_specification));
return make_shared<maps::discarder_by_key>(receiver, std::move(key));
}
abort();

View File

@@ -136,7 +136,7 @@ public:
* be a true column.
* @return the prepared update operation.
*/
virtual ::shared_ptr<operation> prepare(const sstring& keyspace, const column_definition& receiver) = 0;
virtual ::shared_ptr<operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver) = 0;
/**
* @return whether this operation can be applied alongside the {@code
@@ -172,7 +172,7 @@ public:
* @param receiver the "column" this operation applies to.
* @return the prepared delete operation.
*/
virtual ::shared_ptr<operation> prepare(const sstring& keyspace, const column_definition& receiver) = 0;
virtual ::shared_ptr<operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver) = 0;
};
class set_value;
@@ -185,7 +185,7 @@ public:
: _selector(std::move(selector)), _value(std::move(value)) {
}
virtual shared_ptr<operation> prepare(const sstring& keyspace, const column_definition& receiver);
virtual shared_ptr<operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver);
#if 0
protected String toString(ColumnSpecification column)
{
@@ -203,7 +203,7 @@ public:
: _value(value) {
}
virtual shared_ptr<operation> prepare(const sstring& keyspace, const column_definition& receiver) override;
virtual shared_ptr<operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver) override;
#if 0
protected String toString(ColumnSpecification column)
@@ -222,7 +222,7 @@ public:
: _value(value) {
}
virtual shared_ptr<operation> prepare(const sstring& keyspace, const column_definition& receiver) override;
virtual shared_ptr<operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver) override;
#if 0
protected String toString(ColumnSpecification column)
@@ -241,7 +241,7 @@ public:
: _value(std::move(value)) {
}
virtual shared_ptr<operation> prepare(const sstring& keyspace, const column_definition& receiver) override;
virtual shared_ptr<operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver) override;
#if 0
protected String toString(ColumnSpecification column)
@@ -262,7 +262,7 @@ public:
: _id(std::move(id)), _element(std::move(element)) {
}
virtual shared_ptr<column_identifier::raw> affected_column() override;
virtual shared_ptr<operation> prepare(const sstring& keyspace, const column_definition& receiver) override;
virtual shared_ptr<operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver) override;
};
};

View File

@@ -38,7 +38,7 @@ private:
public:
set_value(::shared_ptr<term::raw> value) : _value(std::move(value)) {}
virtual ::shared_ptr <operation> prepare(const sstring& keyspace, const column_definition& receiver) override;
virtual ::shared_ptr <operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver) override;
#if 0
protected String toString(ColumnSpecification column)
@@ -62,7 +62,7 @@ public:
return _id;
}
::shared_ptr<operation> prepare(const sstring& keyspace, const column_definition& receiver) {
::shared_ptr<operation> prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
// No validation, deleting a column is always "well typed"
return ::make_shared<constants::deleter>(receiver);
}

View File

@@ -122,23 +122,23 @@ public:
* @return the <code>Restriction</code> corresponding to this <code>Relation</code>
* @throws InvalidRequestException if this <code>Relation</code> is not valid
*/
virtual ::shared_ptr<restrictions::restriction> to_restriction(schema_ptr schema, ::shared_ptr<variable_specifications> bound_names) final {
virtual ::shared_ptr<restrictions::restriction> to_restriction(database& db, schema_ptr schema, ::shared_ptr<variable_specifications> bound_names) final {
if (_relation_type == operator_type::EQ) {
return new_EQ_restriction(schema, bound_names);
return new_EQ_restriction(db, schema, bound_names);
} else if (_relation_type == operator_type::LT) {
return new_slice_restriction(schema, bound_names, statements::bound::END, false);
return new_slice_restriction(db, schema, bound_names, statements::bound::END, false);
} else if (_relation_type == operator_type::LTE) {
return new_slice_restriction(schema, bound_names, statements::bound::END, true);
return new_slice_restriction(db, schema, bound_names, statements::bound::END, true);
} else if (_relation_type == operator_type::GTE) {
return new_slice_restriction(schema, bound_names, statements::bound::START, true);
return new_slice_restriction(db, schema, bound_names, statements::bound::START, true);
} else if (_relation_type == operator_type::GT) {
return new_slice_restriction(schema, bound_names, statements::bound::START, false);
return new_slice_restriction(db, schema, bound_names, statements::bound::START, false);
} else if (_relation_type == operator_type::IN) {
return new_IN_restriction(schema, bound_names);
return new_IN_restriction(db, schema, bound_names);
} else if (_relation_type == operator_type::CONTAINS) {
return new_contains_restriction(schema, bound_names, false);
return new_contains_restriction(db, schema, bound_names, false);
} else if (_relation_type == operator_type::CONTAINS_KEY) {
return new_contains_restriction(schema, bound_names, true);
return new_contains_restriction(db, schema, bound_names, true);
} else {
throw exceptions::invalid_request_exception(sprint("Unsupported \"!=\" relation: %s", to_string()));
}
@@ -158,7 +158,7 @@ public:
* @return a new EQ restriction instance.
* @throws InvalidRequestException if the relation cannot be converted into an EQ restriction.
*/
virtual ::shared_ptr<restrictions::restriction> new_EQ_restriction(schema_ptr schema,
virtual ::shared_ptr<restrictions::restriction> new_EQ_restriction(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names) = 0;
/**
@@ -169,7 +169,7 @@ public:
* @return a new IN restriction instance
* @throws InvalidRequestException if the relation cannot be converted into an IN restriction.
*/
virtual ::shared_ptr<restrictions::restriction> new_IN_restriction(schema_ptr schema,
virtual ::shared_ptr<restrictions::restriction> new_IN_restriction(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names) = 0;
/**
@@ -182,7 +182,7 @@ public:
* @return a new slice restriction instance
* @throws InvalidRequestException if the <code>Relation</code> is not valid
*/
virtual ::shared_ptr<restrictions::restriction> new_slice_restriction(schema_ptr schema,
virtual ::shared_ptr<restrictions::restriction> new_slice_restriction(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names,
statements::bound bound,
bool inclusive) = 0;
@@ -196,7 +196,7 @@ public:
* @return a new Contains <code>::shared_ptr<restrictions::restriction></code> instance
* @throws InvalidRequestException if the <code>Relation</code> is not valid
*/
virtual ::shared_ptr<restrictions::restriction> new_contains_restriction(schema_ptr schema,
virtual ::shared_ptr<restrictions::restriction> new_contains_restriction(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names, bool isKey) = 0;
protected:
@@ -213,6 +213,7 @@ protected:
*/
virtual ::shared_ptr<term> to_term(const std::vector<::shared_ptr<column_specification>>& receivers,
::shared_ptr<term::raw> raw,
database& db,
const sstring& keyspace,
::shared_ptr<variable_specifications> boundNames) = 0;
@@ -228,11 +229,12 @@ protected:
*/
std::vector<::shared_ptr<term>> to_terms(const std::vector<::shared_ptr<column_specification>>& receivers,
const std::vector<::shared_ptr<term::raw>>& raws,
database& db,
const sstring& keyspace,
::shared_ptr<variable_specifications> boundNames) {
std::vector<::shared_ptr<term>> terms;
for (auto&& r : raws) {
terms.emplace_back(to_term(receivers, r, keyspace, boundNames));
terms.emplace_back(to_term(receivers, r, db, keyspace, boundNames));
}
return terms;
}

View File

@@ -89,7 +89,8 @@ public:
, _nonprimary_key_restrictions(::make_shared<single_column_restrictions>(schema))
{ }
statement_restrictions(schema_ptr schema,
statement_restrictions(database& db,
schema_ptr schema,
const std::vector<::shared_ptr<relation>>& where_clause,
::shared_ptr<variable_specifications> bound_names,
bool selects_only_static_columns,
@@ -105,7 +106,7 @@ public:
*/
if (!where_clause.empty()) {
for (auto&& relation : where_clause) {
add_restriction(relation->to_restriction(schema, bound_names));
add_restriction(relation->to_restriction(db, schema, bound_names));
}
}

View File

@@ -1,103 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.cql3.selection;
import java.nio.ByteBuffer;
import org.apache.cassandra.cql3.selection.Selection.ResultSetBuilder;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.InvalidRequestException;
final class FieldSelector extends Selector
{
private final UserType type;
private final int field;
private final Selector selected;
public static Factory newFactory(final UserType type, final int field, final Selector.Factory factory)
{
return new Factory()
{
protected String getColumnName()
{
return String.format("%s.%s",
factory.getColumnName(),
UTF8Type.instance.getString(type.fieldName(field)));
}
protected AbstractType<?> getReturnType()
{
return type.fieldType(field);
}
public Selector newInstance() throws InvalidRequestException
{
return new FieldSelector(type, field, factory.newInstance());
}
public boolean isAggregateSelectorFactory()
{
return factory.isAggregateSelectorFactory();
}
};
}
public boolean isAggregate()
{
return false;
}
public void addInput(int protocolVersion, ResultSetBuilder rs) throws InvalidRequestException
{
selected.addInput(protocolVersion, rs);
}
public ByteBuffer getOutput(int protocolVersion) throws InvalidRequestException
{
ByteBuffer value = selected.getOutput(protocolVersion);
if (value == null)
return null;
ByteBuffer[] buffers = type.split(value);
return field < buffers.length ? buffers[field] : null;
}
public AbstractType<?> getType()
{
return type.fieldType(field);
}
public void reset()
{
selected.reset();
}
@Override
public String toString()
{
return String.format("%s.%s", selected, UTF8Type.instance.getString(type.fieldName(field)));
}
private FieldSelector(UserType type, int field, Selector selected)
{
this.type = type;
this.field = field;
this.selected = selected;
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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
#include "selector.hh"
#include "types.hh"
namespace cql3 {
namespace selection {
class field_selector : public selector {
user_type _type;
size_t _field;
shared_ptr<selector> _selected;
public:
static shared_ptr<factory> new_factory(user_type type, size_t field, shared_ptr<selector::factory> factory) {
struct field_selector_factory : selector::factory {
user_type _type;
size_t _field;
shared_ptr<selector::factory> _factory;
field_selector_factory(user_type type, size_t field, shared_ptr<selector::factory> factory)
: _type(std::move(type)), _field(field), _factory(std::move(factory)) {
}
virtual sstring column_name() override {
auto&& name = _type->field_name(_field);
auto sname = sstring(reinterpret_cast<const char*>(name.begin()), name.size());
return sprint("%s.%s", _factory->column_name(), sname);
}
virtual data_type get_return_type() override {
return _type->field_type(_field);
}
shared_ptr<selector> new_instance() override {
return make_shared<field_selector>(_type, _field, _factory->new_instance());
}
bool is_aggregate_selector_factory() override {
return _factory->is_aggregate_selector_factory();
}
};
return make_shared<field_selector_factory>(std::move(type), field, std::move(factory));
}
virtual bool is_aggregate() override {
return false;
}
virtual void add_input(serialization_format sf, result_set_builder& rs) override {
_selected->add_input(sf, rs);
}
virtual bytes_opt get_output(serialization_format sf) override {
auto&& value = _selected->get_output(sf);
if (!value) {
return std::experimental::nullopt;
}
auto&& buffers = _type->split(*value);
bytes_opt ret;
if (_field < buffers.size() && buffers[_field]) {
ret = to_bytes(*buffers[_field]);
}
return ret;
}
virtual data_type get_type() override {
return _type->field_type(_field);
}
virtual void reset() {
_selected->reset();
}
virtual sstring assignment_testable_source_context() const override {
auto&& name = _type->field_name(_field);
auto sname = sstring(reinterpret_cast<const char*>(name.begin(), name.size()));
return sprint("%s.%s", _selected, sname);
}
field_selector(user_type type, size_t field, shared_ptr<selector> selected)
: _type(std::move(type)), _field(field), _selected(std::move(selected)) {
}
};
}
}

View File

@@ -3,6 +3,8 @@
*/
#include "selectable.hh"
#include "selectable_with_field_selection.hh"
#include "field_selector.hh"
#include "writetime_or_ttl.hh"
#include "selector_factories.hh"
#include "cql3/functions/functions.hh"
@@ -14,7 +16,7 @@ namespace cql3 {
namespace selection {
shared_ptr<selector::factory>
selectable::writetime_or_ttl::new_selector_factory(schema_ptr s, std::vector<const column_definition*>& defs) {
selectable::writetime_or_ttl::new_selector_factory(database& db, schema_ptr s, std::vector<const column_definition*>& defs) {
auto&& def = s->get_column_definition(_id->name());
if (!def) {
throw exceptions::invalid_request_exception(sprint("Undefined name %s in selection clause", _id));
@@ -44,11 +46,11 @@ selectable::writetime_or_ttl::raw::processes_selection() const {
}
shared_ptr<selector::factory>
selectable::with_function::new_selector_factory(schema_ptr s, std::vector<const column_definition*>& defs) {
auto&& factories = selector_factories::create_factories_and_collect_column_definitions(_args, s, defs);
selectable::with_function::new_selector_factory(database& db, schema_ptr s, std::vector<const column_definition*>& defs) {
auto&& factories = selector_factories::create_factories_and_collect_column_definitions(_args, db, s, defs);
// resolve built-in functions before user defined functions
auto&& fun = functions::functions::get(s->ks_name, _function_name, factories->new_instances(), s->ks_name, s->cf_name);
auto&& fun = functions::functions::get(db, s->ks_name, _function_name, factories->new_instances(), s->ks_name, s->cf_name);
if (!fun) {
throw exceptions::invalid_request_exception(sprint("Unknown function '%s'", _function_name));
}
@@ -74,6 +76,39 @@ selectable::with_function::raw::processes_selection() const {
return true;
}
shared_ptr<selector::factory>
selectable::with_field_selection::new_selector_factory(database& db, schema_ptr s, std::vector<const column_definition*>& defs) {
auto&& factory = _selected->new_selector_factory(db, s, defs);
auto&& type = factory->new_instance()->get_type();
auto&& ut = dynamic_pointer_cast<user_type_impl>(std::move(type));
if (!ut) {
throw exceptions::invalid_request_exception(
sprint("Invalid field selection: %s of type %s is not a user type",
"FIXME: selectable" /* FIMXME: _selected */, ut->as_cql3_type()));
}
for (size_t i = 0; i < ut->size(); ++i) {
if (ut->field_name(i) != _field->bytes_) {
continue;
}
return field_selector::new_factory(std::move(ut), i, std::move(factory));
}
throw exceptions::invalid_request_exception(sprint("%s of type %s has no field %s",
"FIXME: selectable" /* FIXME: _selected */, ut->as_cql3_type(), _field));
}
shared_ptr<selectable>
selectable::with_field_selection::raw::prepare(schema_ptr s) {
// static_pointer_cast<> needed due to lack of covariant return type
// support with smart pointers
return make_shared<with_field_selection>(_selected->prepare(s),
static_pointer_cast<column_identifier>(_field->prepare(s)));
}
bool
selectable::with_field_selection::raw::processes_selection() const {
return true;
}
}
}

View File

@@ -54,7 +54,7 @@ import org.apache.commons.lang3.text.StrBuilder;
class selectable {
public:
virtual ~selectable() {}
virtual ::shared_ptr<selector::factory> new_selector_factory(schema_ptr schema, std::vector<const column_definition*>& defs) = 0;
virtual ::shared_ptr<selector::factory> new_selector_factory(database& db, schema_ptr schema, std::vector<const column_definition*>& defs) = 0;
protected:
static size_t add_and_get_index(const column_definition& def, std::vector<const column_definition*>& defs) {
auto i = std::find(defs.begin(), defs.end(), &def);
@@ -81,71 +81,7 @@ public:
class with_function;
#if 0
public static class WithFieldSelection extends Selectable
{
public final Selectable selected;
public final ColumnIdentifier field;
public WithFieldSelection(Selectable selected, ColumnIdentifier field)
{
this.selected = selected;
this.field = field;
}
@Override
public String toString()
{
return String.format("%s.%s", selected, field);
}
public Selector.Factory newSelectorFactory(CFMetaData cfm,
List<ColumnDefinition> defs) throws InvalidRequestException
{
Selector.Factory factory = selected.newSelectorFactory(cfm, defs);
AbstractType<?> type = factory.newInstance().getType();
if (!(type instanceof UserType))
throw new InvalidRequestException(
String.format("Invalid field selection: %s of type %s is not a user type",
selected,
type.asCQL3Type()));
UserType ut = (UserType) type;
for (int i = 0; i < ut.size(); i++)
{
if (!ut.fieldName(i).equals(field.bytes))
continue;
return FieldSelector.newFactory(ut, i, factory);
}
throw new InvalidRequestException(String.format("%s of type %s has no field %s",
selected,
type.asCQL3Type(),
field));
}
public static class Raw implements Selectable.Raw
{
private final Selectable.Raw selected;
private final ColumnIdentifier.Raw field;
public Raw(Selectable.Raw selected, ColumnIdentifier.Raw field)
{
this.selected = selected;
this.field = field;
}
public WithFieldSelection prepare(CFMetaData cfm)
{
return new WithFieldSelection(selected.prepare(cfm), field.prepare(cfm));
}
public boolean processesSelection()
{
return true;
}
}
}
#endif
class with_field_selection;
};
class selectable::with_function : public selectable {
@@ -168,7 +104,7 @@ public:
}
#endif
virtual shared_ptr<selector::factory> new_selector_factory(schema_ptr s, std::vector<const column_definition*>& defs) override;
virtual shared_ptr<selector::factory> new_selector_factory(database& db, schema_ptr s, std::vector<const column_definition*>& defs) override;
class raw : public selectable::raw {
functions::function_name _function_name;
std::vector<shared_ptr<selectable::raw>> _args;

View File

@@ -0,0 +1,69 @@
/*
* 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
#include "selectable.hh"
#include "cql3/column_identifier.hh"
namespace cql3 {
namespace selection {
class selectable::with_field_selection : public selectable {
public:
shared_ptr<selectable> _selected;
shared_ptr<column_identifier> _field;
public:
with_field_selection(shared_ptr<selectable> selected, shared_ptr<column_identifier> field)
: _selected(std::move(selected)), _field(std::move(field)) {
}
#if 0
@Override
public String toString()
{
return String.format("%s.%s", selected, field);
}
#endif
virtual shared_ptr<selector::factory> new_selector_factory(database& db, schema_ptr s, std::vector<const column_definition*>& defs) override;
class raw : public selectable::raw {
shared_ptr<selectable::raw> _selected;
shared_ptr<column_identifier::raw> _field;
public:
raw(shared_ptr<selectable::raw> selected, shared_ptr<column_identifier::raw> field)
: _selected(std::move(selected)), _field(std::move(field)) {
}
virtual shared_ptr<selectable> prepare(schema_ptr s) override;
virtual bool processes_selection() const override;
};
};
}
}

View File

@@ -198,12 +198,12 @@ uint32_t selection::add_column_for_ordering(const column_definition& c) {
return _columns.size() - 1;
}
::shared_ptr<selection> selection::from_selectors(schema_ptr schema, const std::vector<::shared_ptr<raw_selector>>& raw_selectors) {
::shared_ptr<selection> selection::from_selectors(database& db, schema_ptr schema, const std::vector<::shared_ptr<raw_selector>>& raw_selectors) {
std::vector<const column_definition*> defs;
::shared_ptr<selector_factories> factories =
selector_factories::create_factories_and_collect_column_definitions(
raw_selector::to_selectables(raw_selectors, schema), schema, defs);
raw_selector::to_selectables(raw_selectors, schema), db, schema, defs);
auto metadata = collect_metadata(schema, raw_selectors, *factories);
if (processes_selection(raw_selectors)) {

View File

@@ -167,7 +167,7 @@ private:
static std::vector<::shared_ptr<column_specification>> collect_metadata(schema_ptr schema,
const std::vector<::shared_ptr<raw_selector>>& raw_selectors, const selector_factories& factories);
public:
static ::shared_ptr<selection> from_selectors(schema_ptr schema, const std::vector<::shared_ptr<raw_selector>>& raw_selectors);
static ::shared_ptr<selection> from_selectors(database& db, schema_ptr schema, const std::vector<::shared_ptr<raw_selector>>& raw_selectors);
virtual std::unique_ptr<selectors> new_selectors() = 0;

View File

@@ -87,7 +87,7 @@ public:
*/
virtual void reset() = 0;
virtual assignment_testable::test_result test_assignment(const sstring& keyspace, ::shared_ptr<column_specification> receiver) override {
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) override {
if (receiver->type == get_type()) {
return assignment_testable::test_result::EXACT_MATCH;
} else if (receiver->type->is_value_compatible_with(*get_type())) {

View File

@@ -30,7 +30,8 @@ namespace cql3 {
namespace selection {
selector_factories::selector_factories(std::vector<::shared_ptr<selectable>> selectables, schema_ptr schema,
selector_factories::selector_factories(std::vector<::shared_ptr<selectable>> selectables,
database& db, schema_ptr schema,
std::vector<const column_definition*>& defs)
: _contains_write_time_factory(false)
, _contains_ttl_factory(false)
@@ -39,7 +40,7 @@ selector_factories::selector_factories(std::vector<::shared_ptr<selectable>> sel
_factories.reserve(selectables.size());
for (auto&& selectable : selectables) {
auto factory = selectable->new_selector_factory(schema, defs);
auto factory = selectable->new_selector_factory(db, schema, defs);
_contains_write_time_factory |= factory->is_write_time_selector_factory();
_contains_ttl_factory |= factory->is_ttl_selector_factory();
if (factory->is_aggregate_selector_factory()) {

View File

@@ -69,12 +69,13 @@ public:
*/
static ::shared_ptr<selector_factories> create_factories_and_collect_column_definitions(
std::vector<::shared_ptr<selectable>> selectables,
schema_ptr schema,
database& db, schema_ptr schema,
std::vector<const column_definition*>& defs) {
return ::make_shared<selector_factories>(std::move(selectables), std::move(schema), defs);
return ::make_shared<selector_factories>(std::move(selectables), db, std::move(schema), defs);
}
selector_factories(std::vector<::shared_ptr<selectable>> selectables, schema_ptr schema, std::vector<const column_definition*>& defs);
selector_factories(std::vector<::shared_ptr<selectable>> selectables,
database& db, schema_ptr schema, std::vector<const column_definition*>& defs);
public:
bool uses_function(const sstring& ks_name, const sstring& function_name) const;

View File

@@ -49,7 +49,7 @@ public:
}
#endif
virtual shared_ptr<selector::factory> new_selector_factory(schema_ptr s, std::vector<const column_definition*>& defs) override;
virtual shared_ptr<selector::factory> new_selector_factory(database& db, schema_ptr s, std::vector<const column_definition*>& defs) override;
class raw : public selectable::raw {
shared_ptr<column_identifier::raw> _id;

View File

@@ -16,8 +16,8 @@ sets::value_spec_of(shared_ptr<column_specification> column) {
}
shared_ptr<term>
sets::literal::prepare(const sstring& keyspace, shared_ptr<column_specification> receiver) {
validate_assignable_to(keyspace, receiver);
sets::literal::prepare(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
validate_assignable_to(db, keyspace, receiver);
// We've parsed empty maps as a set literal to break the ambiguity so
// handle that case now
@@ -33,7 +33,7 @@ sets::literal::prepare(const sstring& keyspace, shared_ptr<column_specification>
bool all_terminal = true;
for (shared_ptr<term::raw> rt : _elements)
{
auto t = rt->prepare(keyspace, value_spec);
auto t = rt->prepare(db, keyspace, value_spec);
if (t->contains_bind_marker()) {
throw exceptions::invalid_request_exception(sprint("Invalid set literal for %s: bind variables are not supported inside collection literals", *receiver->name));
@@ -56,7 +56,7 @@ sets::literal::prepare(const sstring& keyspace, shared_ptr<column_specification>
}
void
sets::literal::validate_assignable_to(const sstring& keyspace, shared_ptr<column_specification> receiver) {
sets::literal::validate_assignable_to(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
if (!dynamic_pointer_cast<set_type_impl>(receiver->type)) {
// We've parsed empty maps as a set literal to break the ambiguity so
// handle that case now
@@ -69,14 +69,14 @@ sets::literal::validate_assignable_to(const sstring& keyspace, shared_ptr<column
auto&& value_spec = value_spec_of(receiver);
for (shared_ptr<term::raw> rt : _elements) {
if (!is_assignable(rt->test_assignment(keyspace, value_spec))) {
if (!is_assignable(rt->test_assignment(db, keyspace, value_spec))) {
throw exceptions::invalid_request_exception(sprint("Invalid set literal for %s: value %s is not of type %s", *receiver->name, *rt, *value_spec->type->as_cql3_type()));
}
}
}
assignment_testable::test_result
sets::literal::test_assignment(const sstring& keyspace, shared_ptr<column_specification> receiver) {
sets::literal::test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
if (!dynamic_pointer_cast<set_type_impl>(receiver->type)) {
// We've parsed empty maps as a set literal to break the ambiguity so handle that case now
if (dynamic_pointer_cast<map_type_impl>(receiver->type) && _elements.empty()) {
@@ -94,7 +94,7 @@ sets::literal::test_assignment(const sstring& keyspace, shared_ptr<column_specif
auto&& value_spec = value_spec_of(receiver);
// FIXME: make assignment_testable::test_all() accept ranges
std::vector<shared_ptr<assignment_testable>> to_test(_elements.begin(), _elements.end());
return assignment_testable::test_all(keyspace, value_spec, to_test);
return assignment_testable::test_all(db, keyspace, value_spec, to_test);
}
sstring

View File

@@ -71,10 +71,10 @@ public:
explicit literal(std::vector<shared_ptr<term::raw>> elements)
: _elements(std::move(elements)) {
}
shared_ptr<term> prepare(const sstring& keyspace, shared_ptr<column_specification> receiver);
void validate_assignable_to(const sstring& keyspace, shared_ptr<column_specification> receiver);
shared_ptr<term> prepare(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver);
void validate_assignable_to(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver);
assignment_testable::test_result
test_assignment(const sstring& keyspace, shared_ptr<column_specification> receiver);
test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver);
virtual sstring to_string() const override;
};

View File

@@ -33,20 +33,21 @@ namespace cql3 {
::shared_ptr<term>
single_column_relation::to_term(const std::vector<::shared_ptr<column_specification>>& receivers,
::shared_ptr<term::raw> raw,
database& db,
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]);
auto term = raw->prepare(db, 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) {
single_column_relation::new_EQ_restriction(database& db, schema_ptr schema, ::shared_ptr<variable_specifications> bound_names) {
const 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);
auto term = to_term(to_receivers(schema, column_def), _value, db, schema->ks_name, bound_names);
return ::make_shared<single_column_restriction::EQ>(column_def, std::move(term));
}
fail(unimplemented::cause::COLLECTIONS);
@@ -59,10 +60,10 @@ single_column_relation::new_EQ_restriction(schema_ptr schema, ::shared_ptr<varia
}
::shared_ptr<restrictions::restriction>
single_column_relation::new_IN_restriction(schema_ptr schema, ::shared_ptr<variable_specifications> bound_names) {
single_column_relation::new_IN_restriction(database& db, schema_ptr schema, ::shared_ptr<variable_specifications> bound_names) {
const column_definition& column_def = to_column_definition(schema, _entity);
auto receivers = to_receivers(schema, column_def);
auto terms = to_terms(receivers, _in_values, schema->ks_name, bound_names);
auto terms = to_terms(receivers, _in_values, db, schema->ks_name, bound_names);
if (terms.empty()) {
fail(unimplemented::cause::COLLECTIONS);
#if 0

View File

@@ -95,7 +95,7 @@ public:
}
protected:
virtual ::shared_ptr<term> to_term(const std::vector<::shared_ptr<column_specification>>& receivers,
::shared_ptr<term::raw> raw, const sstring& keyspace,
::shared_ptr<term::raw> raw, database& db, const sstring& keyspace,
::shared_ptr<variable_specifications> bound_names) override;
#if 0
@@ -124,22 +124,22 @@ protected:
}
protected:
virtual ::shared_ptr<restrictions::restriction> new_EQ_restriction(schema_ptr schema,
virtual ::shared_ptr<restrictions::restriction> new_EQ_restriction(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names);
virtual ::shared_ptr<restrictions::restriction> new_IN_restriction(schema_ptr schema,
virtual ::shared_ptr<restrictions::restriction> new_IN_restriction(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names) override;
virtual ::shared_ptr<restrictions::restriction> new_slice_restriction(schema_ptr schema,
virtual ::shared_ptr<restrictions::restriction> new_slice_restriction(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names,
statements::bound bound,
bool inclusive) override {
auto&& column_def = to_column_definition(schema, _entity);
auto term = to_term(to_receivers(schema, column_def), _value, schema->ks_name, std::move(bound_names));
auto term = to_term(to_receivers(schema, column_def), _value, db, schema->ks_name, std::move(bound_names));
return ::make_shared<restrictions::single_column_restriction::slice>(column_def, bound, inclusive, std::move(term));
}
virtual shared_ptr<restrictions::restriction> new_contains_restriction(schema_ptr schema,
virtual shared_ptr<restrictions::restriction> new_contains_restriction(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names,
bool is_key) override {
throw std::runtime_error("not implemented");

View File

@@ -350,7 +350,7 @@ public:
statements.push_back(parsed->prepare(db, bound_names));
}
auto&& prep_attrs = _attrs->prepare("[batch]", "[batch]");
auto&& prep_attrs = _attrs->prepare(db, "[batch]", "[batch]");
prep_attrs->collect_marker_specification(bound_names);
batch_statement batch_statement_(bound_names->size(), _type, std::move(statements), std::move(prep_attrs));

View File

@@ -52,7 +52,7 @@ void delete_statement::add_update_for_key(mutation& m, const exploded_clustering
}
::shared_ptr<modification_statement>
delete_statement::parsed::prepare_internal(schema_ptr schema, ::shared_ptr<variable_specifications> bound_names,
delete_statement::parsed::prepare_internal(database& db, schema_ptr schema, ::shared_ptr<variable_specifications> bound_names,
std::unique_ptr<attributes> attrs) {
auto stmt = ::make_shared<delete_statement>(statement_type::DELETE, bound_names->size(), schema, std::move(attrs));
@@ -70,12 +70,12 @@ delete_statement::parsed::prepare_internal(schema_ptr schema, ::shared_ptr<varia
throw exceptions::invalid_request_exception(sprint("Invalid identifier %s for deletion (should not be a PRIMARY KEY part)", def->name_as_text()));
}
auto&& op = deletion->prepare(schema->ks_name, *def);
auto&& op = deletion->prepare(db, schema->ks_name, *def);
op->collect_marker_specification(bound_names);
stmt->add_operation(op);
}
stmt->process_where_clause(_where_clause, std::move(bound_names));
stmt->process_where_clause(db, _where_clause, std::move(bound_names));
return stmt;
}

View File

@@ -99,7 +99,7 @@ public:
, _where_clause(std::move(where_clause))
{ }
protected:
virtual ::shared_ptr<modification_statement> prepare_internal(schema_ptr schema,
virtual ::shared_ptr<modification_statement> prepare_internal(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs);
};
};

View File

@@ -411,7 +411,7 @@ modification_statement::add_key_value(const column_definition& def, ::shared_ptr
}
void
modification_statement::process_where_clause(std::vector<relation_ptr> where_clause, ::shared_ptr<variable_specifications> names) {
modification_statement::process_where_clause(database& db, std::vector<relation_ptr> where_clause, ::shared_ptr<variable_specifications> names) {
for (auto&& relation : where_clause) {
if (relation->is_multi_column()) {
throw exceptions::invalid_request_exception(sprint("Multi-column relations cannot be used in WHERE clauses for UPDATE and DELETE statements: %s", relation->to_string()));
@@ -430,7 +430,7 @@ modification_statement::process_where_clause(std::vector<relation_ptr> where_cla
if (def->is_primary_key()) {
if (rel->is_EQ() || (def->is_partition_key() && rel->is_IN())) {
add_key_values(*def, rel->to_restriction(s, std::move(names)));
add_key_values(*def, rel->to_restriction(db, s, std::move(names)));
} else {
throw exceptions::invalid_request_exception(sprint("Invalid operator %s for PRIMARY KEY part %s", rel->get_operator(), def->name_as_text()));
}
@@ -451,10 +451,10 @@ modification_statement::parsed::prepare(database& db) {
modification_statement::parsed::prepare(database& db, ::shared_ptr<variable_specifications> bound_names) {
schema_ptr schema = validation::validate_column_family(db, keyspace(), column_family());
auto prepared_attributes = _attrs->prepare(keyspace(), column_family());
auto prepared_attributes = _attrs->prepare(db, keyspace(), column_family());
prepared_attributes->collect_marker_specification(bound_names);
::shared_ptr<modification_statement> stmt = prepare_internal(schema, bound_names, std::move(prepared_attributes));
::shared_ptr<modification_statement> stmt = prepare_internal(db, schema, bound_names, std::move(prepared_attributes));
if (_if_not_exists || _if_exists || !_conditions.empty()) {
if (stmt->is_counter()) {
@@ -482,7 +482,7 @@ modification_statement::parsed::prepare(database& db, ::shared_ptr<variable_spec
throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *id));
}
auto condition = entry.second->prepare(keyspace(), *def);
auto condition = entry.second->prepare(db, keyspace(), *def);
condition->collect_marker_specificaton(bound_names);
if (def->is_primary_key()) {

View File

@@ -223,7 +223,7 @@ private:
public:
void add_key_value(const column_definition& def, ::shared_ptr<term> value);
void process_where_clause(std::vector<relation_ptr> where_clause, ::shared_ptr<variable_specifications> names);
void process_where_clause(database& db, std::vector<relation_ptr> where_clause, ::shared_ptr<variable_specifications> names);
std::vector<partition_key> build_partition_keys(const query_options& options);
private:
@@ -437,7 +437,7 @@ public:
virtual ::shared_ptr<parsed_statement::prepared> prepare(database& db) override;
::shared_ptr<modification_statement> prepare(database& db, ::shared_ptr<variable_specifications> bound_names);;
protected:
virtual ::shared_ptr<modification_statement> prepare_internal(schema_ptr schema,
virtual ::shared_ptr<modification_statement> prepare_internal(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs) = 0;
};
};

View File

@@ -331,9 +331,9 @@ select_statement::raw_statement::prepare(database& db) {
auto selection = _select_clause.empty()
? selection::selection::wildcard(schema)
: selection::selection::from_selectors(schema, _select_clause);
: selection::selection::from_selectors(db, schema, _select_clause);
auto restrictions = prepare_restrictions(schema, bound_names, selection);
auto restrictions = prepare_restrictions(db, schema, bound_names, selection);
if (_parameters->is_distinct()) {
validate_distinct_selection(schema, selection, restrictions);
@@ -361,18 +361,18 @@ select_statement::raw_statement::prepare(database& db) {
std::move(restrictions),
is_reversed_,
std::move(ordering_comparator),
prepare_limit(bound_names));
prepare_limit(db, bound_names));
return ::make_shared<parsed_statement::prepared>(std::move(stmt), std::move(*bound_names));
}
::shared_ptr<restrictions::statement_restrictions>
select_statement::raw_statement::prepare_restrictions(schema_ptr schema,
select_statement::raw_statement::prepare_restrictions(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names,
::shared_ptr<selection::selection> selection)
{
try {
return ::make_shared<restrictions::statement_restrictions>(schema, std::move(_where_clause), bound_names,
return ::make_shared<restrictions::statement_restrictions>(db, schema, std::move(_where_clause), bound_names,
selection->contains_only_static_columns(), selection->contains_a_collection());
} catch (const exceptions::unrecognized_entity_exception& e) {
if (contains_alias(e.entity)) {
@@ -384,12 +384,12 @@ select_statement::raw_statement::prepare_restrictions(schema_ptr schema,
/** Returns a ::shared_ptr<term> for the limit or null if no limit is set */
::shared_ptr<term>
select_statement::raw_statement::prepare_limit(::shared_ptr<variable_specifications> bound_names) {
select_statement::raw_statement::prepare_limit(database& db, ::shared_ptr<variable_specifications> bound_names) {
if (!_limit) {
return {};
}
auto prep_limit = _limit->prepare(keyspace(), limit_receiver());
auto prep_limit = _limit->prepare(db, keyspace(), limit_receiver());
prep_limit->collect_marker_specification(bound_names);
return prep_limit;
}

View File

@@ -456,12 +456,13 @@ public:
virtual ::shared_ptr<prepared> prepare(database& db) override;
private:
::shared_ptr<restrictions::statement_restrictions> prepare_restrictions(
database& db,
schema_ptr schema,
::shared_ptr<variable_specifications> bound_names,
::shared_ptr<selection::selection> selection);
/** Returns a ::shared_ptr<term> for the limit or null if no limit is set */
::shared_ptr<term> prepare_limit(::shared_ptr<variable_specifications> bound_names);
::shared_ptr<term> prepare_limit(database& db, ::shared_ptr<variable_specifications> bound_names);
static void verify_ordering_is_allowed(::shared_ptr<restrictions::statement_restrictions> restrictions);

View File

@@ -88,7 +88,7 @@ void update_statement::add_update_for_key(mutation& m, const exploded_clustering
}
::shared_ptr<modification_statement>
update_statement::parsed_insert::prepare_internal(schema_ptr schema,
update_statement::parsed_insert::prepare_internal(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
auto stmt = ::make_shared<update_statement>(statement_type::INSERT, bound_names->size(), schema, std::move(attrs));
@@ -123,11 +123,11 @@ update_statement::parsed_insert::prepare_internal(schema_ptr schema,
auto&& value = _column_values[i];
if (def->is_primary_key()) {
auto t = value->prepare(keyspace(), def->column_specification);
auto t = value->prepare(db, keyspace(), def->column_specification);
t->collect_marker_specification(bound_names);
stmt->add_key_value(*def, std::move(t));
} else {
auto operation = operation::set_value(value).prepare(keyspace(), *def);
auto operation = operation::set_value(value).prepare(db, keyspace(), *def);
operation->collect_marker_specification(bound_names);
stmt->add_operation(std::move(operation));
};
@@ -136,7 +136,7 @@ update_statement::parsed_insert::prepare_internal(schema_ptr schema,
}
::shared_ptr<modification_statement>
update_statement::parsed_update::prepare_internal(schema_ptr schema,
update_statement::parsed_update::prepare_internal(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
auto stmt = ::make_shared<update_statement>(statement_type::UPDATE, bound_names->size(), schema, std::move(attrs));
@@ -148,7 +148,7 @@ update_statement::parsed_update::prepare_internal(schema_ptr schema,
throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *entry.first));
}
auto operation = entry.second->prepare(keyspace(), *def);
auto operation = entry.second->prepare(db, keyspace(), *def);
operation->collect_marker_specification(bound_names);
if (def->is_primary_key()) {
@@ -157,7 +157,7 @@ update_statement::parsed_update::prepare_internal(schema_ptr schema,
stmt->add_operation(std::move(operation));
}
stmt->process_where_clause(_where_clause, bound_names);
stmt->process_where_clause(db, _where_clause, bound_names);
return stmt;
}

View File

@@ -99,7 +99,7 @@ public:
, _column_values{std::move(column_values)}
{ }
virtual ::shared_ptr<modification_statement> prepare_internal(schema_ptr schema,
virtual ::shared_ptr<modification_statement> prepare_internal(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs) override;
};
@@ -129,7 +129,7 @@ public:
, _where_clause(std::move(where_clause))
{ }
protected:
virtual ::shared_ptr<modification_statement> prepare_internal(schema_ptr schema,
virtual ::shared_ptr<modification_statement> prepare_internal(database& db, schema_ptr schema,
::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs);
};
};

View File

@@ -115,7 +115,7 @@ public:
* case this RawTerm describe a list index or a map key, etc...
* @return the prepared term.
*/
virtual ::shared_ptr<term> prepare(const sstring& keyspace, ::shared_ptr<column_specification> receiver) = 0;
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, ::shared_ptr<column_specification> receiver) = 0;
virtual sstring to_string() const = 0;
@@ -131,7 +131,7 @@ public:
class multi_column_raw : public virtual raw {
public:
virtual ::shared_ptr<term> prepare(const sstring& keyspace, const std::vector<shared_ptr<column_specification>>& receiver) = 0;
virtual ::shared_ptr<term> prepare(database& db, const sstring& keyspace, const std::vector<shared_ptr<column_specification>>& receiver) = 0;
};
};

View File

@@ -47,12 +47,12 @@ public:
literal(std::vector<shared_ptr<raw>> elements)
: _elements(std::move(elements)) {
}
virtual shared_ptr<term> prepare(const sstring& keyspace, shared_ptr<column_specification> receiver) override {
validate_assignable_to(keyspace, receiver);
virtual shared_ptr<term> prepare(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) override {
validate_assignable_to(db, keyspace, receiver);
std::vector<shared_ptr<term>> values;
bool all_terminal = true;
for (size_t i = 0; i < _elements.size(); ++i) {
auto&& value = _elements[i]->prepare(keyspace, component_spec_of(receiver, i));
auto&& value = _elements[i]->prepare(db, keyspace, component_spec_of(receiver, i));
if (dynamic_pointer_cast<non_terminal>(value)) {
all_terminal = false;
}
@@ -66,7 +66,7 @@ public:
}
}
virtual shared_ptr<term> prepare(const sstring& keyspace, const std::vector<shared_ptr<column_specification>>& receivers) override {
virtual shared_ptr<term> prepare(database& db, const sstring& keyspace, const std::vector<shared_ptr<column_specification>>& receivers) override {
if (_elements.size() != receivers.size()) {
throw exceptions::invalid_request_exception(sprint("Expected %d elements in value tuple, but got %d: %s", receivers.size(), _elements.size(), *this));
}
@@ -75,7 +75,7 @@ public:
std::vector<data_type> types;
bool all_terminal = true;
for (size_t i = 0; i < _elements.size(); ++i) {
auto&& t = _elements[i]->prepare(keyspace, receivers[i]);
auto&& t = _elements[i]->prepare(db, keyspace, receivers[i]);
if (dynamic_pointer_cast<non_terminal>(t)) {
all_terminal = false;
}
@@ -91,7 +91,7 @@ public:
}
private:
void validate_assignable_to(const sstring& keyspace, shared_ptr<column_specification> receiver) {
void validate_assignable_to(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
auto tt = dynamic_pointer_cast<tuple_type_impl>(receiver->type);
if (!tt) {
throw exceptions::invalid_request_exception(sprint("Invalid tuple type literal for %s of type %s", receiver->name, receiver->type->as_cql3_type()));
@@ -104,15 +104,15 @@ public:
auto&& value = _elements[i];
auto&& spec = component_spec_of(receiver, i);
if (!assignment_testable::is_assignable(value->test_assignment(keyspace, spec))) {
if (!assignment_testable::is_assignable(value->test_assignment(db, keyspace, spec))) {
throw exceptions::invalid_request_exception(sprint("Invalid tuple literal for %s: component %d is not of type %s", receiver->name, i, spec->type->as_cql3_type()));
}
}
}
public:
virtual assignment_testable::test_result test_assignment(const sstring& keyspace, shared_ptr<column_specification> receiver) override {
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) override {
try {
validate_assignable_to(keyspace, receiver);
validate_assignable_to(db, keyspace, receiver);
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
} catch (exceptions::invalid_request_exception e) {
return assignment_testable::test_result::NOT_ASSIGNABLE;

View File

@@ -35,24 +35,24 @@ public:
: _type(std::move(type)), _term(std::move(term)) {
}
virtual shared_ptr<term> prepare(const sstring& keyspace, shared_ptr<column_specification> receiver) override {
if (!is_assignable(_term->test_assignment(keyspace, casted_spec_of(keyspace, receiver)))) {
virtual shared_ptr<term> prepare(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) override {
if (!is_assignable(_term->test_assignment(db, keyspace, casted_spec_of(db, keyspace, receiver)))) {
throw exceptions::invalid_request_exception(sprint("Cannot cast value %s to type %s", _term, _type));
}
if (!is_assignable(test_assignment(keyspace, receiver))) {
if (!is_assignable(test_assignment(db, keyspace, receiver))) {
throw exceptions::invalid_request_exception(sprint("Cannot assign value %s to %s of type %s", *this, receiver->name, receiver->type->as_cql3_type()));
}
return _term->prepare(keyspace, receiver);
return _term->prepare(db, keyspace, receiver);
}
private:
shared_ptr<column_specification> casted_spec_of(const sstring& keyspace, shared_ptr<column_specification> receiver) {
shared_ptr<column_specification> casted_spec_of(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
return make_shared<column_specification>(receiver->ks_name, receiver->cf_name,
make_shared<column_identifier>(to_string(), true), _type->prepare(keyspace)->get_type());
make_shared<column_identifier>(to_string(), true), _type->prepare(db, keyspace)->get_type());
}
public:
virtual assignment_testable::test_result test_assignment(const sstring& keyspace, shared_ptr<column_specification> receiver) override {
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) override {
try {
auto&& casted_type = _type->prepare(keyspace)->get_type();
auto&& casted_type = _type->prepare(db, keyspace)->get_type();
if (receiver->type->equals(casted_type)) {
return assignment_testable::test_result::EXACT_MATCH;
} else if (receiver->type->is_value_compatible_with(*casted_type)) {

191
cql3/user_types.hh Normal file
View File

@@ -0,0 +1,191 @@
/*
* 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.
*/
/*
* Modified by Cloudius Systems
*
* Copyright 2015 Cloudius Systems
*/
#pragma once
#include "column_specification.hh"
#include "term.hh"
#include "column_identifier.hh"
#include "constants.hh"
#include "to_string.hh"
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/count.hpp>
#include <boost/algorithm/cxx11/any_of.hpp>
namespace cql3 {
/**
* Static helper methods and classes for user types.
*/
class user_types {
user_types() = delete;
public:
static shared_ptr<column_specification> field_spec_of(shared_ptr<column_specification> column, size_t field) {
auto&& ut = static_pointer_cast<user_type_impl>(column->type);
auto&& name = ut->field_name(field);
auto&& sname = sstring(reinterpret_cast<const char*>(name.data()), name.size());
return make_shared<column_specification>(
column->ks_name,
column->cf_name,
make_shared<column_identifier>(column->name->to_string() + "." + sname, true),
ut->field_type(field));
}
class literal : public term::raw {
public:
using elements_map_type = std::unordered_map<column_identifier, shared_ptr<term::raw>>;
elements_map_type _entries;
literal(elements_map_type entries)
: _entries(std::move(entries)) {
}
virtual shared_ptr<term> prepare(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) override {
validate_assignable_to(db, keyspace, receiver);
auto&& ut = static_pointer_cast<user_type_impl>(receiver->type);
bool all_terminal = true;
std::vector<shared_ptr<term>> values;
values.reserve(_entries.size());
size_t found_values = 0;
for (size_t i = 0; i < ut->size(); ++i) {
auto&& field = column_identifier(to_bytes(ut->field_name(i)), utf8_type);
auto iraw = _entries.find(field);
shared_ptr<term::raw> raw;
if (iraw == _entries.end()) {
raw = cql3::constants::NULL_LITERAL;
} else {
raw = iraw->second;
++found_values;
}
auto&& value = raw->prepare(db, keyspace, field_spec_of(receiver, i));
if (dynamic_cast<non_terminal*>(value.get())) {
all_terminal = false;
}
values.push_back(std::move(value));
}
if (found_values != _entries.size()) {
// We had some field that are not part of the type
for (auto&& id_val : _entries) {
auto&& id = id_val.first;
if (!boost::range::count(ut->field_names(), id.bytes_)) {
throw exceptions::invalid_request_exception(sprint("Unknown field '%s' in value of user defined type %s", id, ut->get_name_as_string()));
}
}
}
delayed_value value(ut, values);
if (all_terminal) {
return value.bind(query_options::DEFAULT);
} else {
return make_shared(std::move(value));
}
}
private:
void validate_assignable_to(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) {
auto&& ut = dynamic_pointer_cast<user_type_impl>(receiver->type);
if (!ut) {
throw exceptions::invalid_request_exception(sprint("Invalid user type literal for %s of type %s", receiver->name, receiver->type->as_cql3_type()));
}
for (size_t i = 0; i < ut->size(); i++) {
column_identifier field(to_bytes(ut->field_name(i)), utf8_type);
if (_entries.count(field) == 0) {
continue;
}
shared_ptr<term::raw> value = _entries[field];
auto&& field_spec = field_spec_of(receiver, i);
if (!assignment_testable::is_assignable(value->test_assignment(db, keyspace, field_spec))) {
throw exceptions::invalid_request_exception(sprint("Invalid user type literal for %s: field %s is not of type %s", receiver->name, field, field_spec->type->as_cql3_type()));
}
}
}
public:
virtual assignment_testable::test_result test_assignment(database& db, const sstring& keyspace, shared_ptr<column_specification> receiver) override {
try {
validate_assignable_to(db, keyspace, receiver);
return assignment_testable::test_result::WEAKLY_ASSIGNABLE;
} catch (exceptions::invalid_request_exception& e) {
return assignment_testable::test_result::NOT_ASSIGNABLE;
}
}
virtual sstring assignment_testable_source_context() const override {
return to_string();
}
virtual sstring to_string() const override {
auto kv_to_str = [] (auto&& kv) { return sprint("%s:%s", kv.first, kv.second); };
return sprint("{%s}", ::join(", ", _entries | boost::adaptors::transformed(kv_to_str)));
}
};
// Same purpose than Lists.DelayedValue, except we do handle bind marker in that case
class delayed_value : public non_terminal {
user_type _type;
std::vector<shared_ptr<term>> _values;
public:
delayed_value(user_type type, std::vector<shared_ptr<term>> values)
: _type(std::move(type)), _values(std::move(values)) {
}
virtual bool uses_function(const sstring& ks_name, const sstring& function_name) const override {
return boost::algorithm::any_of(_values,
std::bind(&term::uses_function, std::placeholders::_1, std::cref(ks_name), std::cref(function_name)));
}
virtual bool contains_bind_marker() const override {
return boost::algorithm::any_of(_values, std::mem_fn(&term::contains_bind_marker));
}
virtual void collect_marker_specification(shared_ptr<variable_specifications> bound_names) {
for (auto&& v : _values) {
v->collect_marker_specification(bound_names);
}
}
private:
std::vector<bytes_opt> bind_internal(const query_options& options) {
auto sf = options.get_serialization_format();
std::vector<bytes_opt> buffers;
for (size_t i = 0; i < _type->size(); ++i) {
buffers.push_back(_values[i]->bind_and_get(options));
// Inside UDT values, we must force the serialization of collections to v3 whatever protocol
// version is in use since we're going to store directly that serialized value.
if (sf != serialization_format::use_32_bit() && _type->field_type(i)->is_collection() && buffers.back()) {
auto&& ctype = static_pointer_cast<collection_type_impl>(_type->field_type(i));
buffers.back() = ctype->reserialize(sf, serialization_format::use_32_bit(), bytes_view(*buffers.back()));
}
}
return buffers;
}
public:
virtual shared_ptr<terminal> bind(const query_options& options) override {
return ::make_shared<constants::value>(bind_and_get(options));
}
virtual bytes_opt bind_and_get(const query_options& options) override {
return user_type_impl::build_value(bind_internal(options));
}
};
};
}

View File

@@ -771,3 +771,36 @@ SEASTAR_TEST_CASE(test_tuples) {
});
});
}
SEASTAR_TEST_CASE(test_user_type) {
auto make_user_type = [] {
return user_type_impl::get_instance("ks", to_bytes("ut1"),
{to_bytes("my_int"), to_bytes("my_bigint"), to_bytes("my_text")},
{int32_type, long_type, utf8_type});
};
return do_with_cql_env([make_user_type] (cql_test_env& e) {
return e.create_table([make_user_type] (auto ks_name) {
// CQL: "create table cf (id int primary key, t tuple<int, bigint, text>);
return schema({}, ks_name, "cf",
{{"id", int32_type}}, {}, {{"t", make_user_type()}}, {}, utf8_type);
}).then([&e] {
return e.execute_cql("insert into cf (id, t) values (1, (1001, 2001, 'abc1'));").discard_result();
}).then([&e] {
return e.execute_cql("select t.my_int, t.my_bigint, t.my_text from cf where id = 1;");
}).then([&e] (shared_ptr<transport::messages::result_message> msg) {
assert_that(msg).is_rows()
.with_rows({
{int32_type->decompose(int32_t(1001)), long_type->decompose(int64_t(2001)), utf8_type->decompose(sstring("abc1"))},
});
}).then([&e] {
return e.execute_cql("update cf set t = { my_int: 1002, my_bigint: 2002, my_text: 'abc2' } where id = 1;").discard_result();
}).then([&e] {
return e.execute_cql("select t.my_int, t.my_bigint, t.my_text from cf where id = 1;");
}).then([&e] (shared_ptr<transport::messages::result_message> msg) {
assert_that(msg).is_rows()
.with_rows({
{int32_type->decompose(int32_t(1002)), long_type->decompose(int64_t(2002)), utf8_type->decompose(sstring("abc2"))},
});
});
});
}

View File

@@ -1484,13 +1484,17 @@ list_type_impl::cql3_type_name() const {
return sprint("list<%s>", _elements->as_cql3_type());
}
tuple_type_impl::tuple_type_impl(std::vector<data_type> types)
: abstract_type(make_name(types)), _types(std::move(types)) {
tuple_type_impl::tuple_type_impl(sstring name, std::vector<data_type> types)
: abstract_type(std::move(name)), _types(std::move(types)) {
for (auto& t : _types) {
t = t->freeze();
}
}
tuple_type_impl::tuple_type_impl(std::vector<data_type> types)
: tuple_type_impl(make_name(types), std::move(types)) {
}
shared_ptr<tuple_type_impl>
tuple_type_impl::get_instance(std::vector<data_type> types) {
return ::make_shared<tuple_type_impl>(std::move(types));
@@ -1631,6 +1635,29 @@ tuple_type_impl::make_name(const std::vector<data_type>& types) {
return sprint("tuple<%s>", ::join(", ", types | boost::adaptors::transformed(std::mem_fn(&abstract_type::name))));
}
sstring
user_type_impl::get_name_as_string() const {
return boost::any_cast<sstring>(utf8_type->compose(_name));
}
shared_ptr<cql3::cql3_type>
user_type_impl::as_cql3_type() {
throw "not yet";
}
sstring
user_type_impl::make_name(sstring keyspace, bytes name, std::vector<bytes> field_names, std::vector<data_type> field_types) {
std::ostringstream os;
os << "(" << keyspace << "," << to_hex(name);
for (size_t i = 0; i < field_names.size(); ++i) {
os << ",";
os << to_hex(field_names[i]) << ":";
os << field_types[i]->name(); // FIXME: ignore frozen<>
}
os << ")";
return os.str();
}
thread_local const shared_ptr<abstract_type> int32_type(make_shared<int32_type_impl>());
thread_local const shared_ptr<abstract_type> long_type(make_shared<long_type_impl>());
thread_local const shared_ptr<abstract_type> ascii_type(make_shared<string_type_impl>("ascii", [] { return cql3::cql3_type::ascii; }));

View File

@@ -839,6 +839,7 @@ protected:
static boost::iterator_range<tuple_deserializing_iterator> make_range(bytes_view v) {
return { tuple_deserializing_iterator::start(v), tuple_deserializing_iterator::finish(v) };
}
tuple_type_impl(sstring name, std::vector<data_type> types);
public:
using native_type = std::vector<boost::any>;
tuple_type_impl(std::vector<data_type> types);
@@ -889,3 +890,31 @@ private:
// FIXME: conflicts with another tuple_type
using db_tuple_type = shared_ptr<tuple_type_impl>;
class user_type_impl : public tuple_type_impl {
public:
const sstring _keyspace;
const bytes _name;
private:
std::vector<bytes> _field_names;
public:
user_type_impl(sstring keyspace, bytes name, std::vector<bytes> field_names, std::vector<data_type> field_types)
: tuple_type_impl(make_name(keyspace, name, field_names, field_types), field_types)
, _keyspace(keyspace)
, _name(name)
, _field_names(field_names) {
}
static shared_ptr<user_type_impl> get_instance(sstring keyspace, bytes name, std::vector<bytes> field_names, std::vector<data_type> field_types) {
return ::make_shared<user_type_impl>(std::move(keyspace), std::move(name), std::move(field_names), std::move(field_types));
}
data_type field_type(size_t i) const { return type(i); }
const std::vector<data_type>& field_types() const { return _types; }
bytes_view field_name(size_t i) const { return _field_names[i]; }
const std::vector<bytes>& field_names() const { return _field_names; }
sstring get_name_as_string() const;
virtual shared_ptr<cql3::cql3_type> as_cql3_type() override;
private:
static sstring make_name(sstring keyspace, bytes name, std::vector<bytes> field_names, std::vector<data_type> field_types);
};
using user_type = shared_ptr<user_type_impl>;