diff --git a/configure.py b/configure.py index b7a16d93f2..0066e88e86 100755 --- a/configure.py +++ b/configure.py @@ -251,6 +251,7 @@ urchin_core = (['database.cc', 'cql3/constants.cc', 'cql3/query_processor.cc', 'cql3/query_options.cc', + 'cql3/single_column_relation.cc', 'db/db.cc', 'io/io.cc', 'utils/utils.cc', diff --git a/cql3/single_column_relation.cc b/cql3/single_column_relation.cc new file mode 100644 index 0000000000..308e61e23c --- /dev/null +++ b/cql3/single_column_relation.cc @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright 2015 Cloudius Systems + * + * Modified by Cloudius Systems + */ + +#include "cql3/single_column_relation.hh" +#include "cql3/restrictions/single_column_restriction.hh" +#include "unimplemented.hh" + +using namespace cql3::restrictions; + +namespace cql3 { + +::shared_ptr +single_column_relation::to_term(std::vector<::shared_ptr> receivers, + ::shared_ptr raw, + const sstring& keyspace, + ::shared_ptr bound_names) +{ + // TODO: optimize vector away, accept single column_specification + assert(receivers.size() == 1); + auto term = raw->prepare(keyspace, receivers[0]); + term->collect_marker_specification(bound_names); + return term; +} + +::shared_ptr +single_column_relation::new_EQ_restriction(schema_ptr schema, ::shared_ptr bound_names) +{ + column_definition& column_def = to_column_definition(schema, _entity); + if (!_map_key) { + auto term = to_term(to_receivers(schema, column_def), _value, schema->ks_name, bound_names); + return ::make_shared(column_def, std::move(term)); + } + unimplemented::collections(); +#if 0 + List receivers = toReceivers(schema, columnDef); + Term entryKey = toTerm(Collections.singletonList(receivers.get(0)), map_key, schema.ksName, bound_names); + Term entryValue = toTerm(Collections.singletonList(receivers.get(1)), value, schema.ksName, bound_names); + return new SingleColumnRestriction.Contains(columnDef, entryKey, entryValue); +#endif +} + +std::vector<::shared_ptr> +single_column_relation::to_receivers(schema_ptr schema, column_definition& column_def) +{ + auto receiver = column_def.column_specification; + + if (column_def.is_compact_value()) { + throw exceptions::invalid_request_exception(sprint( + "Predicates on the non-primary-key column (%s) of a COMPACT table are not yet supported", column_def.name_as_text())); + } + + if (is_IN()) { + // For partition keys we only support IN for the last name so far + if (column_def.is_partition_key() && !schema->is_last_partition_key(column_def)) { + throw exceptions::invalid_request_exception(sprint( + "Partition KEY part %s cannot be restricted by IN relation (only the last part of the partition key can)", + column_def.name_as_text())); + } + + // We only allow IN on the row key and the clustering key so far, never on non-PK columns, and this even if + // there's an index + // Note: for backward compatibility reason, we conside a IN of 1 value the same as a EQ, so we let that + // slide. + if (!column_def.is_primary_key() && !can_have_only_one_value()) { + throw exceptions::invalid_request_exception(sprint( + "IN predicates on non-primary-key columns (%s) is not yet supported", column_def.name_as_text())); + } + } else if (is_slice()) { + // Non EQ relation is not supported without token(), even if we have a 2ndary index (since even those + // are ordered by partitioner). + // Note: In theory we could allow it for 2ndary index queries with ALLOW FILTERING, but that would + // probably require some special casing + // Note bis: This is also why we don't bother handling the 'tuple' notation of #4851 for keys. If we + // lift the limitation for 2ndary + // index with filtering, we'll need to handle it though. + if (column_def.is_partition_key()) { + throw exceptions::invalid_request_exception( + "Only EQ and IN relation are supported on the partition key (unless you use the token() function)"); + } + } + + if (is_contains_key()) { + unimplemented::collections(); +#if 0 + if (!(receiver.type instanceof MapType)) { + throw exceptions::invalid_request_exception(sprint("Cannot use CONTAINS KEY on non-map column %s", receiver.name_as_text())); + } +#endif + } + + if (_map_key) { + unimplemented::collections(); +#if 0 + checkFalse(receiver.type instanceof ListType, "Indexes on list entries (%s[index] = value) are not currently supported.", receiver.name); + checkTrue(receiver.type instanceof MapType, "Column %s cannot be used as a map", receiver.name); + checkTrue(receiver.type.isMultiCell(), "Map-entry equality predicates on frozen map column %s are not supported", receiver.name); + checkTrue(isEQ(), "Only EQ relations are supported on map entries"); +#endif + } + + if (receiver->type->is_collection()) { + unimplemented::collections(); +#if 0 + // We don't support relations against entire collections (unless they're frozen), like "numbers = {1, 2, 3}" + checkFalse(receiver.type.isMultiCell() && !isLegalRelationForNonFrozenCollection(), + "Collection column '%s' (%s) cannot be restricted by a '%s' relation", + receiver.name, + receiver.type.asCQL3Type(), + get_operator()); + + if (isContainsKey() || isContains()) + { + receiver = makeCollectionReceiver(receiver, isContainsKey()); + } + else if (receiver.type.isMultiCell() && map_key != null && isEQ()) + { + List receivers = new ArrayList<>(2); + receivers.add(makeCollectionReceiver(receiver, true)); + receivers.add(makeCollectionReceiver(receiver, false)); + return receivers; + } +#endif + } + + return {std::move(receiver)}; +} + +} diff --git a/cql3/single_column_relation.hh b/cql3/single_column_relation.hh index 8272fc6f02..6431f880f8 100644 --- a/cql3/single_column_relation.hh +++ b/cql3/single_column_relation.hh @@ -77,7 +77,7 @@ public: * @param value the value being compared. */ single_column_relation(::shared_ptr entity, const operator_type& type, ::shared_ptr value) - : single_column_relation(std::move(entity), {}, std::move(type), std::move(value)) + : single_column_relation(std::move(entity), {}, type, std::move(value)) { } #if 0 @@ -95,22 +95,12 @@ public: ::shared_ptr get_map_key() { return _map_key; } +protected: + ::shared_ptr to_term(std::vector<::shared_ptr> receivers, + ::shared_ptr raw, const sstring& keyspace, + ::shared_ptr bound_names); #if 0 - @Override - protected Term toTerm(List receivers, - Raw raw, - String keyspace, - ::shared_ptr boundNames) - throws InvalidRequestException - { - assert receivers.size() == 1; - - Term term = raw.prepare(keyspace, receivers.get(0)); - term.collectMarkerSpecification(boundNames); - return term; - } - public SingleColumnRelation withNonStrictOperator() { switch (relationType) @@ -137,21 +127,7 @@ public: protected: virtual ::shared_ptr new_EQ_restriction(schema_ptr schema, - ::shared_ptr bound_names) override { - throw std::runtime_error("not implemented"); -#if 0 - ColumnDefinition columnDef = toColumnDefinition(schema, entity); - if (map_key == null) - { - Term term = toTerm(toReceivers(schema, columnDef), value, schema.ksName, bound_names); - return new SingleColumnRestriction.EQ(columnDef, term); - } - List receivers = toReceivers(schema, columnDef); - Term entryKey = toTerm(Collections.singletonList(receivers.get(0)), map_key, schema.ksName, bound_names); - Term entryValue = toTerm(Collections.singletonList(receivers.get(1)), value, schema.ksName, bound_names); - return new SingleColumnRestriction.Contains(columnDef, entryKey, entryValue); -#endif - } + ::shared_ptr bound_names); virtual ::shared_ptr new_IN_restriction(schema_ptr schema, ::shared_ptr bound_names) override { @@ -192,84 +168,18 @@ protected: #endif } -#if 0 +private: /** * Returns the receivers for this relation. * * @param schema the Column Family meta data - * @param columnDef the column definition + * @param column_def the column definition * @return the receivers for the specified relation. - * @throws InvalidRequestException if the relation is invalid + * @throws exceptions::invalid_request_exception if the relation is invalid */ - private List toReceivers(schema_ptr schema, ColumnDefinition columnDef) throws InvalidRequestException - { - ColumnSpecification receiver = columnDef; - - checkFalse(columnDef.isCompactValue(), - "Predicates on the non-primary-key column (%s) of a COMPACT table are not yet supported", - columnDef.name); - - if (isIN()) - { - // For partition keys we only support IN for the last name so far - checkFalse(columnDef.isPartitionKey() && !isLastPartitionKey(schema, columnDef), - "Partition KEY part %s cannot be restricted by IN relation (only the last part of the partition key can)", - columnDef.name); - - // We only allow IN on the row key and the clustering key so far, never on non-PK columns, and this even if - // there's an index - // Note: for backward compatibility reason, we conside a IN of 1 value the same as a EQ, so we let that - // slide. - checkFalse(!columnDef.isPrimaryKeyColumn() && !canHaveOnlyOneValue(), - "IN predicates on non-primary-key columns (%s) is not yet supported", columnDef.name); - } - else if (isSlice()) - { - // Non EQ relation is not supported without token(), even if we have a 2ndary index (since even those - // are ordered by partitioner). - // Note: In theory we could allow it for 2ndary index queries with ALLOW FILTERING, but that would - // probably require some special casing - // Note bis: This is also why we don't bother handling the 'tuple' notation of #4851 for keys. If we - // lift the limitation for 2ndary - // index with filtering, we'll need to handle it though. - checkFalse(columnDef.isPartitionKey(), "Only EQ and IN relation are supported on the partition key (unless you use the token() function)"); - } - - checkFalse(isContainsKey() && !(receiver.type instanceof MapType), "Cannot use CONTAINS KEY on non-map column %s", receiver.name); - - if (map_key != null) - { - checkFalse(receiver.type instanceof ListType, "Indexes on list entries (%s[index] = value) are not currently supported.", receiver.name); - checkTrue(receiver.type instanceof MapType, "Column %s cannot be used as a map", receiver.name); - checkTrue(receiver.type.isMultiCell(), "Map-entry equality predicates on frozen map column %s are not supported", receiver.name); - checkTrue(isEQ(), "Only EQ relations are supported on map entries"); - } - - if (receiver.type.isCollection()) - { - // We don't support relations against entire collections (unless they're frozen), like "numbers = {1, 2, 3}" - checkFalse(receiver.type.isMultiCell() && !isLegalRelationForNonFrozenCollection(), - "Collection column '%s' (%s) cannot be restricted by a '%s' relation", - receiver.name, - receiver.type.asCQL3Type(), - get_operator()); - - if (isContainsKey() || isContains()) - { - receiver = makeCollectionReceiver(receiver, isContainsKey()); - } - else if (receiver.type.isMultiCell() && map_key != null && isEQ()) - { - List receivers = new ArrayList<>(2); - receivers.add(makeCollectionReceiver(receiver, true)); - receivers.add(makeCollectionReceiver(receiver, false)); - return receivers; - } - } - - return Collections.singletonList(receiver); - } + std::vector<::shared_ptr> to_receivers(schema_ptr schema, column_definition& column_def); +#if 0 private ColumnSpecification makeCollectionReceiver(ColumnSpecification receiver, bool forKey) { return ((CollectionType) receiver.type).makeCollectionReceiver(receiver, forKey); @@ -284,25 +194,12 @@ protected: { return map_key != null && isEQ(); } - - /** - * Checks if the specified column is the last column of the partition key. - * - * @param schema the column family meta data - * @param columnDef the column to check - * @return true if the specified column is the last column of the partition key, false - * otherwise. - */ - private static bool isLastPartitionKey(schema_ptr schema, ColumnDefinition columnDef) - { - return columnDef.position() == schema.partitionKeyColumns().size() - 1; - } - - private bool canHaveOnlyOneValue() - { - return isEQ() || (isIN() && in_values != null && in_values.size() == 1); - } #endif + +private: + bool can_have_only_one_value() { + return is_EQ() || (is_IN() && _in_values.size() == 1); + } }; }; diff --git a/unimplemented.hh b/unimplemented.hh index 063ce3d7bc..6a64dcdcc2 100644 --- a/unimplemented.hh +++ b/unimplemented.hh @@ -50,6 +50,14 @@ void triggers() { warn("triggers"); } +static inline +void collections() __attribute__((noreturn)); + +static inline +void collections() { + fail("collections"); +} + static inline void metrics() {}