mirror of https://github.com/apache/cassandra
Allow range deletions in CQL
patch by Benjamin Lerer; reviewed by Joshua McKenzie for CASSANDRA-6237
This commit is contained in:
parent
8f249a67d9
commit
2e3727e3ff
6
NEWS.txt
6
NEWS.txt
|
|
@ -18,6 +18,11 @@ using the provided 'sstableupgrade' tool.
|
|||
|
||||
New features
|
||||
------------
|
||||
- Support for IN restrictions on any partition key component or clustering key
|
||||
as well as support for EQ and IN multicolumn restrictions has been added to
|
||||
UPDATE and DELETE statement.
|
||||
- Support for single-column and multi-colum slice restrictions (>, >=, <= and <)
|
||||
has been added to DELETE statements
|
||||
- nodetool rebuild_index accepts the index argument without
|
||||
the redundant table name
|
||||
- Materialized Views, which allow for server-side denormalization, is now
|
||||
|
|
@ -35,7 +40,6 @@ New features
|
|||
you do not run repair for a long time, you will keep all tombstones around which
|
||||
can cause other problems.
|
||||
|
||||
|
||||
Upgrading
|
||||
---------
|
||||
- Max mutation size is now configurable via max_mutation_size_in_kb setting in
|
||||
|
|
|
|||
|
|
@ -863,8 +863,11 @@ bc(syntax)..
|
|||
<where-clause> ::= <relation> ( AND <relation> )*
|
||||
|
||||
<relation> ::= <identifier> '=' <term>
|
||||
| <identifier> IN '(' ( <term> ( ',' <term> )* )? ')'
|
||||
| '(' <identifier> (',' <identifier>)* ')' '=' <term-tuple>
|
||||
| <identifier> IN '(' ( <term> ( ',' <term>)* )? ')'
|
||||
| <identifier> IN '?'
|
||||
| '(' <identifier> (',' <identifier>)* ')' IN '(' ( <term-tuple> ( ',' <term-tuple>)* )? ')'
|
||||
| '(' <identifier> (',' <identifier>)* ')' IN '?'
|
||||
|
||||
<option> ::= TIMESTAMP <integer>
|
||||
| TTL <integer>
|
||||
|
|
@ -914,10 +917,14 @@ bc(syntax)..
|
|||
|
||||
<where-clause> ::= <relation> ( AND <relation> )*
|
||||
|
||||
<relation> ::= <identifier> '=' <term>
|
||||
| <identifier> IN '(' ( <term> ( ',' <term> )* )? ')'
|
||||
<relation> ::= <identifier> <op> <term>
|
||||
| '(' <identifier> (',' <identifier>)* ')' <op> <term-tuple>
|
||||
| <identifier> IN '(' ( <term> ( ',' <term>)* )? ')'
|
||||
| <identifier> IN '?'
|
||||
| '(' <identifier> (',' <identifier>)* ')' IN '(' ( <term-tuple> ( ',' <term-tuple>)* )? ')'
|
||||
| '(' <identifier> (',' <identifier>)* ')' IN '?'
|
||||
|
||||
<op> ::= '=' | '<' | '>' | '<=' | '>='
|
||||
<condition> ::= <identifier> '=' <term>
|
||||
| <identifier> '[' <term> ']' '=' <term>
|
||||
p.
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import java.util.*;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Collections2;
|
||||
|
||||
import org.apache.cassandra.cql3.*;
|
||||
import org.apache.cassandra.db.rows.*;
|
||||
|
|
@ -285,9 +285,9 @@ public class ColumnDefinition extends ColumnSpecification implements Comparable<
|
|||
* @param definitions the column definitions to convert.
|
||||
* @return the column identifiers corresponding to the specified definitions
|
||||
*/
|
||||
public static List<ColumnIdentifier> toIdentifiers(List<ColumnDefinition> definitions)
|
||||
public static Collection<ColumnIdentifier> toIdentifiers(Collection<ColumnDefinition> definitions)
|
||||
{
|
||||
return Lists.transform(definitions, new Function<ColumnDefinition, ColumnIdentifier>()
|
||||
return Collections2.transform(definitions, new Function<ColumnDefinition, ColumnIdentifier>()
|
||||
{
|
||||
@Override
|
||||
public ColumnIdentifier apply(ColumnDefinition columnDef)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.util.Collections;
|
||||
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.cql3.functions.Function;
|
||||
|
||||
/**
|
||||
* Base class for <code>Conditions</code> classes.
|
||||
*
|
||||
*/
|
||||
abstract class AbstractConditions implements Conditions
|
||||
{
|
||||
public Iterable<Function> getFunctions()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public Iterable<ColumnDefinition> getColumns()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean appliesToStaticColumns()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean appliesToRegularColumns()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isIfExists()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isIfNotExists()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.cql3.functions.Function;
|
||||
import org.apache.cassandra.cql3.statements.CQL3CasRequest;
|
||||
import org.apache.cassandra.db.Clustering;
|
||||
|
||||
import static java.util.stream.StreamSupport.stream;
|
||||
|
||||
/**
|
||||
* A set of <code>ColumnCondition</code>s.
|
||||
*
|
||||
*/
|
||||
public final class ColumnConditions extends AbstractConditions
|
||||
{
|
||||
/**
|
||||
* The conditions on regular columns.
|
||||
*/
|
||||
private final List<ColumnCondition> columnConditions;
|
||||
|
||||
/**
|
||||
* The conditions on static columns
|
||||
*/
|
||||
private final List<ColumnCondition> staticConditions;
|
||||
|
||||
/**
|
||||
* Creates a new <code>ColumnConditions</code> instance for the specified builder.
|
||||
*/
|
||||
private ColumnConditions(Builder builder)
|
||||
{
|
||||
this.columnConditions = builder.columnConditions;
|
||||
this.staticConditions = builder.staticConditions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesToStaticColumns()
|
||||
{
|
||||
return !staticConditions.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesToRegularColumns()
|
||||
{
|
||||
return !columnConditions.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ColumnDefinition> getColumns()
|
||||
{
|
||||
return Stream.concat(columnConditions.stream(), staticConditions.stream())
|
||||
.map(e -> e.column)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return columnConditions.isEmpty() && staticConditions.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the conditions to the specified CAS request.
|
||||
*
|
||||
* @param request the request
|
||||
* @param clustering the clustering prefix
|
||||
* @param options the query options
|
||||
*/
|
||||
public void addConditionsTo(CQL3CasRequest request,
|
||||
Clustering clustering,
|
||||
QueryOptions options)
|
||||
{
|
||||
if (!columnConditions.isEmpty())
|
||||
request.addConditions(clustering, columnConditions, options);
|
||||
if (!staticConditions.isEmpty())
|
||||
request.addConditions(Clustering.STATIC_CLUSTERING, staticConditions, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Function> getFunctions()
|
||||
{
|
||||
return Stream.concat(columnConditions.stream(), staticConditions.stream())
|
||||
.flatMap(e -> stream(e.getFunctions().spliterator(), false))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>Builder</code> for <code>ColumnConditions</code>.
|
||||
* @return a new <code>Builder</code> for <code>ColumnConditions</code>
|
||||
*/
|
||||
public static Builder newBuilder()
|
||||
{
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* A <code>Builder</code> for <code>ColumnConditions</code>.
|
||||
*
|
||||
*/
|
||||
public static final class Builder
|
||||
{
|
||||
/**
|
||||
* The conditions on regular columns.
|
||||
*/
|
||||
private List<ColumnCondition> columnConditions = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* The conditions on static columns
|
||||
*/
|
||||
private List<ColumnCondition> staticConditions = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Adds the specified <code>ColumnCondition</code> to this set of conditions.
|
||||
* @param condition the condition to add
|
||||
*/
|
||||
public Builder add(ColumnCondition condition)
|
||||
{
|
||||
List<ColumnCondition> conds = null;
|
||||
if (condition.column.isStatic())
|
||||
{
|
||||
if (staticConditions.isEmpty())
|
||||
staticConditions = new ArrayList<>();
|
||||
conds = staticConditions;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (columnConditions.isEmpty())
|
||||
columnConditions = new ArrayList<>();
|
||||
conds = columnConditions;
|
||||
}
|
||||
conds.add(condition);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColumnConditions build()
|
||||
{
|
||||
return new ColumnConditions(this);
|
||||
}
|
||||
|
||||
private Builder()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -194,12 +194,18 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select
|
|||
* once the comparator is known with prepare(). This should only be used with identifiers that are actual
|
||||
* column names. See CASSANDRA-8178 for more background.
|
||||
*/
|
||||
public static class Raw implements Selectable.Raw
|
||||
public static interface Raw extends Selectable.Raw
|
||||
{
|
||||
|
||||
public ColumnIdentifier prepare(CFMetaData cfm);
|
||||
}
|
||||
|
||||
public static class Literal implements Raw
|
||||
{
|
||||
private final String rawText;
|
||||
private final String text;
|
||||
|
||||
public Raw(String rawText, boolean keepCase)
|
||||
public Literal(String rawText, boolean keepCase)
|
||||
{
|
||||
this.rawText = rawText;
|
||||
this.text = keepCase ? rawText : rawText.toLowerCase(Locale.US);
|
||||
|
|
@ -239,9 +245,10 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select
|
|||
@Override
|
||||
public final boolean equals(Object o)
|
||||
{
|
||||
if(!(o instanceof ColumnIdentifier.Raw))
|
||||
if(!(o instanceof Literal))
|
||||
return false;
|
||||
ColumnIdentifier.Raw that = (ColumnIdentifier.Raw)o;
|
||||
|
||||
Literal that = (Literal) o;
|
||||
return text.equals(that.text);
|
||||
}
|
||||
|
||||
|
|
@ -251,4 +258,45 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select
|
|||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ColumnIdentifierValue implements Raw
|
||||
{
|
||||
private final ColumnIdentifier identifier;
|
||||
|
||||
public ColumnIdentifierValue(ColumnIdentifier identifier)
|
||||
{
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public ColumnIdentifier prepare(CFMetaData cfm)
|
||||
{
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public boolean processesSelection()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode()
|
||||
{
|
||||
return identifier.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o)
|
||||
{
|
||||
if(!(o instanceof ColumnIdentifierValue))
|
||||
return false;
|
||||
ColumnIdentifierValue that = (ColumnIdentifierValue) o;
|
||||
return identifier.equals(that.identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return identifier.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.cql3.functions.Function;
|
||||
import org.apache.cassandra.cql3.statements.CQL3CasRequest;
|
||||
import org.apache.cassandra.db.Clustering;
|
||||
|
||||
/**
|
||||
* Conditions that can be applied to a mutation statement.
|
||||
*
|
||||
*/
|
||||
public interface Conditions
|
||||
{
|
||||
/**
|
||||
* An EMPTY condition
|
||||
*/
|
||||
static final Conditions EMPTY_CONDITION = ColumnConditions.newBuilder().build();
|
||||
|
||||
/**
|
||||
* IF EXISTS condition
|
||||
*/
|
||||
static final Conditions IF_EXISTS_CONDITION = new IfExistsCondition();
|
||||
|
||||
/**
|
||||
* IF NOT EXISTS condition
|
||||
*/
|
||||
static final Conditions IF_NOT_EXISTS_CONDITION = new IfNotExistsCondition();
|
||||
|
||||
/**
|
||||
* Returns the functions used by the conditions.
|
||||
* @return the functions used by the conditions
|
||||
*/
|
||||
Iterable<Function> getFunctions();
|
||||
|
||||
/**
|
||||
* Returns the column definitions to which apply the conditions.
|
||||
* @return the column definitions to which apply the conditions.
|
||||
*/
|
||||
Iterable<ColumnDefinition> getColumns();
|
||||
|
||||
/**
|
||||
* Checks if this <code>Conditions</code> is empty.
|
||||
* @return <code>true</code> if this <code>Conditions</code> is empty, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Checks if this is a IF EXIST condition.
|
||||
* @return <code>true</code> if this is a IF EXIST condition, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isIfExists();
|
||||
|
||||
/**
|
||||
* Checks if this is a IF NOT EXIST condition.
|
||||
* @return <code>true</code> if this is a IF NOT EXIST condition, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isIfNotExists();
|
||||
|
||||
/**
|
||||
* Checks if some of the conditions apply to static columns.
|
||||
*
|
||||
* @return <code>true</code> if some of the conditions apply to static columns, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean appliesToStaticColumns();
|
||||
|
||||
/**
|
||||
* Checks if some of the conditions apply to regular columns.
|
||||
*
|
||||
* @return <code>true</code> if some of the conditions apply to regular columns, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean appliesToRegularColumns();
|
||||
|
||||
/**
|
||||
* Adds the conditions to the specified CAS request.
|
||||
*
|
||||
* @param request the request
|
||||
* @param clustering the clustering prefix
|
||||
* @param options the query options
|
||||
*/
|
||||
public void addConditionsTo(CQL3CasRequest request,
|
||||
Clustering clustering,
|
||||
QueryOptions options);
|
||||
}
|
||||
|
|
@ -1154,9 +1154,9 @@ userPassword[RoleOptions opts]
|
|||
// identifiers because the underlying comparator is not necessarily text. See
|
||||
// CASSANDRA-8178 for details.
|
||||
cident returns [ColumnIdentifier.Raw id]
|
||||
: t=IDENT { $id = new ColumnIdentifier.Raw($t.text, false); }
|
||||
| t=QUOTED_NAME { $id = new ColumnIdentifier.Raw($t.text, true); }
|
||||
| k=unreserved_keyword { $id = new ColumnIdentifier.Raw(k, false); }
|
||||
: t=IDENT { $id = new ColumnIdentifier.Literal($t.text, false); }
|
||||
| t=QUOTED_NAME { $id = new ColumnIdentifier.Literal($t.text, true); }
|
||||
| k=unreserved_keyword { $id = new ColumnIdentifier.Literal(k, false); }
|
||||
;
|
||||
|
||||
// Column identifiers where the comparator is known to be text
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 org.apache.cassandra.cql3.statements.CQL3CasRequest;
|
||||
import org.apache.cassandra.db.Clustering;
|
||||
|
||||
final class IfExistsCondition extends AbstractConditions
|
||||
{
|
||||
@Override
|
||||
public void addConditionsTo(CQL3CasRequest request, Clustering clustering, QueryOptions options)
|
||||
{
|
||||
request.addExist(clustering);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIfExists()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 org.apache.cassandra.cql3.statements.CQL3CasRequest;
|
||||
import org.apache.cassandra.db.Clustering;
|
||||
|
||||
final class IfNotExistsCondition extends AbstractConditions
|
||||
{
|
||||
@Override
|
||||
public void addConditionsTo(CQL3CasRequest request, Clustering clustering, QueryOptions options)
|
||||
{
|
||||
request.addNotExist(clustering);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIfNotExists()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ public class Json
|
|||
|
||||
public Prepared prepareAndCollectMarkers(CFMetaData metadata, Collection<ColumnDefinition> receivers, VariableSpecifications boundNames)
|
||||
{
|
||||
return new PreparedLiteral(metadata.ksName, parseJson(text, receivers));
|
||||
return new PreparedLiteral(parseJson(text, receivers));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ public class Json
|
|||
public Prepared prepareAndCollectMarkers(CFMetaData metadata, Collection<ColumnDefinition> receivers, VariableSpecifications boundNames)
|
||||
{
|
||||
boundNames.add(bindIndex, makeReceiver(metadata));
|
||||
return new PreparedMarker(metadata.ksName, bindIndex, receivers);
|
||||
return new PreparedMarker(bindIndex, receivers);
|
||||
}
|
||||
|
||||
private ColumnSpecification makeReceiver(CFMetaData metadata)
|
||||
|
|
@ -105,27 +105,7 @@ public class Json
|
|||
*/
|
||||
public static abstract class Prepared
|
||||
{
|
||||
private final String keyspace;
|
||||
|
||||
protected Prepared(String keyspace)
|
||||
{
|
||||
this.keyspace = keyspace;
|
||||
}
|
||||
|
||||
protected abstract Term.Raw getRawTermForColumn(ColumnDefinition def);
|
||||
|
||||
public Term getPrimaryKeyValueForColumn(ColumnDefinition def)
|
||||
{
|
||||
// Note that we know we don't have to call collectMarkerSpecification since it has already been collected
|
||||
return getRawTermForColumn(def).prepare(keyspace, def);
|
||||
}
|
||||
|
||||
public Operation getSetOperationForColumn(ColumnDefinition def)
|
||||
{
|
||||
// Note that we know we don't have to call collectMarkerSpecification on the operation since we have
|
||||
// already collected all we need.
|
||||
return new Operation.SetValue(getRawTermForColumn(def)).prepare(keyspace, def);
|
||||
}
|
||||
public abstract Term.Raw getRawTermForColumn(ColumnDefinition def);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -135,13 +115,12 @@ public class Json
|
|||
{
|
||||
private final Map<ColumnIdentifier, Term> columnMap;
|
||||
|
||||
public PreparedLiteral(String keyspace, Map<ColumnIdentifier, Term> columnMap)
|
||||
public PreparedLiteral(Map<ColumnIdentifier, Term> columnMap)
|
||||
{
|
||||
super(keyspace);
|
||||
this.columnMap = columnMap;
|
||||
}
|
||||
|
||||
protected Term.Raw getRawTermForColumn(ColumnDefinition def)
|
||||
public Term.Raw getRawTermForColumn(ColumnDefinition def)
|
||||
{
|
||||
Term value = columnMap.get(def.name);
|
||||
return value == null ? Constants.NULL_LITERAL : new ColumnValue(value);
|
||||
|
|
@ -158,14 +137,13 @@ public class Json
|
|||
|
||||
private Map<ColumnIdentifier, Term> columnMap;
|
||||
|
||||
public PreparedMarker(String keyspace, int bindIndex, Collection<ColumnDefinition> columns)
|
||||
public PreparedMarker(int bindIndex, Collection<ColumnDefinition> columns)
|
||||
{
|
||||
super(keyspace);
|
||||
this.bindIndex = bindIndex;
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
protected DelayedColumnValue getRawTermForColumn(ColumnDefinition def)
|
||||
public DelayedColumnValue getRawTermForColumn(ColumnDefinition def)
|
||||
{
|
||||
return new DelayedColumnValue(this, def);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cassandra.cql3.functions.Function;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Iterators;
|
||||
|
||||
/**
|
||||
* A set of <code>Operation</code>s.
|
||||
*
|
||||
*/
|
||||
public final class Operations implements Iterable<Operation>
|
||||
{
|
||||
/**
|
||||
* The operations on regular columns.
|
||||
*/
|
||||
private final List<Operation> regularOperations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The operations on static columns.
|
||||
*/
|
||||
private final List<Operation> staticOperations = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Checks if some of the operations apply to static columns.
|
||||
*
|
||||
* @return <code>true</code> if some of the operations apply to static columns, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean appliesToStaticColumns()
|
||||
{
|
||||
return !staticOperations.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if some of the operations apply to regular columns.
|
||||
*
|
||||
* @return <code>true</code> if some of the operations apply to regular columns, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean appliesToRegularColumns()
|
||||
{
|
||||
return !regularOperations.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operation on regular columns.
|
||||
* @return the operation on regular columns
|
||||
*/
|
||||
public List<Operation> regularOperations()
|
||||
{
|
||||
return regularOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operation on static columns.
|
||||
* @return the operation on static columns
|
||||
*/
|
||||
public List<Operation> staticOperations()
|
||||
{
|
||||
return staticOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified <code>Operation</code> to this set of operations.
|
||||
* @param operation the operation to add
|
||||
*/
|
||||
public void add(Operation operation)
|
||||
{
|
||||
if (operation.column.isStatic())
|
||||
staticOperations.add(operation);
|
||||
else
|
||||
regularOperations.add(operation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if one of the operations requires a read.
|
||||
*
|
||||
* @return <code>true</code> if one of the operations requires a read, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean requiresRead()
|
||||
{
|
||||
// Lists SET operation incurs a read.
|
||||
for (Operation operation : this)
|
||||
if (operation.requiresRead())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this <code>Operations</code> is empty.
|
||||
* @return <code>true</code> if this <code>Operations</code> is empty, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return staticOperations.isEmpty() && regularOperations.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Operation> iterator()
|
||||
{
|
||||
return Iterators.concat(staticOperations.iterator(), regularOperations.iterator());
|
||||
}
|
||||
|
||||
public Iterable<? extends Function> getFunctions()
|
||||
{
|
||||
List<Function> functions = new ArrayList<>();
|
||||
for (Operation operation : this)
|
||||
Iterables.addAll(functions, operation.getFunctions());
|
||||
return functions;
|
||||
}
|
||||
}
|
||||
|
|
@ -223,6 +223,7 @@ public final class SingleColumnRelation extends Relation
|
|||
}
|
||||
|
||||
checkFalse(isContainsKey() && !(receiver.type instanceof MapType), "Cannot use CONTAINS KEY on non-map column %s", receiver.name);
|
||||
checkFalse(isContains() && !(receiver.type.isCollection()), "Cannot use CONTAINS on non-collection column %s", receiver.name);
|
||||
|
||||
if (mapKey != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -216,9 +216,14 @@ public class UpdateParameters
|
|||
return deletionTime;
|
||||
}
|
||||
|
||||
public RangeTombstone makeRangeTombstone(CBuilder cbuilder)
|
||||
public RangeTombstone makeRangeTombstone(ClusteringComparator comparator, Clustering clustering)
|
||||
{
|
||||
return new RangeTombstone(cbuilder.buildSlice(), deletionTime);
|
||||
return makeRangeTombstone(Slice.make(comparator, clustering));
|
||||
}
|
||||
|
||||
public RangeTombstone makeRangeTombstone(Slice slice)
|
||||
{
|
||||
return new RangeTombstone(slice, deletionTime);
|
||||
}
|
||||
|
||||
public Row getPrefetchedRow(DecoratedKey key, Clustering clustering)
|
||||
|
|
|
|||
|
|
@ -25,13 +25,16 @@ import com.google.common.collect.Iterables;
|
|||
|
||||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.cql3.*;
|
||||
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||
import org.apache.cassandra.cql3.QueryOptions;
|
||||
import org.apache.cassandra.cql3.Relation;
|
||||
import org.apache.cassandra.cql3.VariableSpecifications;
|
||||
import org.apache.cassandra.cql3.functions.Function;
|
||||
import org.apache.cassandra.cql3.statements.Bound;
|
||||
import org.apache.cassandra.cql3.statements.StatementType;
|
||||
import org.apache.cassandra.db.*;
|
||||
import org.apache.cassandra.db.filter.RowFilter;
|
||||
import org.apache.cassandra.dht.*;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.index.SecondaryIndexManager;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.btree.BTreeSet;
|
||||
|
|
@ -48,6 +51,11 @@ public final class StatementRestrictions
|
|||
{
|
||||
public static final String NO_INDEX_FOUND_MESSAGE =
|
||||
"No supported secondary index found for the non primary key columns restrictions";
|
||||
/**
|
||||
* The type of statement
|
||||
*/
|
||||
private final StatementType type;
|
||||
|
||||
/**
|
||||
* The Column Family meta data
|
||||
*/
|
||||
|
|
@ -86,30 +94,33 @@ public final class StatementRestrictions
|
|||
/**
|
||||
* Creates a new empty <code>StatementRestrictions</code>.
|
||||
*
|
||||
* @param type the type of statement
|
||||
* @param cfm the column family meta data
|
||||
* @return a new empty <code>StatementRestrictions</code>.
|
||||
*/
|
||||
public static StatementRestrictions empty(CFMetaData cfm)
|
||||
public static StatementRestrictions empty(StatementType type, CFMetaData cfm)
|
||||
{
|
||||
return new StatementRestrictions(cfm);
|
||||
return new StatementRestrictions(type, cfm);
|
||||
}
|
||||
|
||||
private StatementRestrictions(CFMetaData cfm)
|
||||
private StatementRestrictions(StatementType type, CFMetaData cfm)
|
||||
{
|
||||
this.type = type;
|
||||
this.cfm = cfm;
|
||||
this.partitionKeyRestrictions = new PrimaryKeyRestrictionSet(cfm.getKeyValidatorAsClusteringComparator(), true);
|
||||
this.clusteringColumnsRestrictions = new PrimaryKeyRestrictionSet(cfm.comparator, false);
|
||||
this.nonPrimaryKeyRestrictions = new RestrictionSet();
|
||||
}
|
||||
|
||||
public StatementRestrictions(CFMetaData cfm,
|
||||
public StatementRestrictions(StatementType type,
|
||||
CFMetaData cfm,
|
||||
List<Relation> whereClause,
|
||||
VariableSpecifications boundNames,
|
||||
boolean selectsOnlyStaticColumns,
|
||||
boolean selectACollection,
|
||||
boolean useFiltering) throws InvalidRequestException
|
||||
boolean useFiltering)
|
||||
{
|
||||
this(cfm);
|
||||
this(type, cfm);
|
||||
|
||||
/*
|
||||
* WHERE clause. For a given entity, rules are:
|
||||
|
|
@ -123,13 +134,19 @@ public final class StatementRestrictions
|
|||
for (Relation relation : whereClause)
|
||||
addRestriction(relation.toRestriction(cfm, boundNames));
|
||||
|
||||
ColumnFamilyStore cfs = Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfName);
|
||||
SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
|
||||
boolean hasQueriableClusteringColumnIndex = false;
|
||||
boolean hasQueriableIndex = false;
|
||||
|
||||
boolean hasQueriableClusteringColumnIndex = clusteringColumnsRestrictions.hasSupportingIndex(secondaryIndexManager);
|
||||
boolean hasQueriableIndex = hasQueriableClusteringColumnIndex
|
||||
|| partitionKeyRestrictions.hasSupportingIndex(secondaryIndexManager)
|
||||
|| nonPrimaryKeyRestrictions.hasSupportingIndex(secondaryIndexManager);
|
||||
if (type.allowUseOfSecondaryIndices())
|
||||
{
|
||||
ColumnFamilyStore cfs = Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfName);
|
||||
SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
|
||||
|
||||
hasQueriableClusteringColumnIndex = clusteringColumnsRestrictions.hasSupportingIndex(secondaryIndexManager);
|
||||
hasQueriableIndex = hasQueriableClusteringColumnIndex
|
||||
|| partitionKeyRestrictions.hasSupportingIndex(secondaryIndexManager)
|
||||
|| nonPrimaryKeyRestrictions.hasSupportingIndex(secondaryIndexManager);
|
||||
}
|
||||
|
||||
// At this point, the select statement if fully constructed, but we still have a few things to validate
|
||||
processPartitionKeyRestrictions(hasQueriableIndex);
|
||||
|
|
@ -139,10 +156,26 @@ public final class StatementRestrictions
|
|||
if (usesSecondaryIndexing)
|
||||
indexRestrictions.add(partitionKeyRestrictions);
|
||||
|
||||
checkFalse(selectsOnlyStaticColumns && hasClusteringColumnsRestriction(),
|
||||
"Cannot restrict clustering columns when selecting only static columns");
|
||||
if (selectsOnlyStaticColumns && hasClusteringColumnsRestriction())
|
||||
{
|
||||
// If the only updated/deleted columns are static, then we don't need clustering columns.
|
||||
// And in fact, unless it is an INSERT, we reject if clustering colums are provided as that
|
||||
// suggest something unintended. For instance, given:
|
||||
// CREATE TABLE t (k int, v int, s int static, PRIMARY KEY (k, v))
|
||||
// it can make sense to do:
|
||||
// INSERT INTO t(k, v, s) VALUES (0, 1, 2)
|
||||
// but both
|
||||
// UPDATE t SET s = 3 WHERE k = 0 AND v = 1
|
||||
// DELETE v FROM t WHERE k = 0 AND v = 1
|
||||
// sounds like you don't really understand what your are doing.
|
||||
if (type.isDelete() || type.isUpdate())
|
||||
throw invalidRequest("Invalid restrictions on clustering columns since the %s statement modifies only static columns",
|
||||
type);
|
||||
if (type.isSelect())
|
||||
throw invalidRequest("Cannot restrict clustering columns when selecting only static columns");
|
||||
}
|
||||
|
||||
processClusteringColumnsRestrictions(hasQueriableIndex, selectACollection);
|
||||
processClusteringColumnsRestrictions(hasQueriableIndex, selectsOnlyStaticColumns, selectACollection);
|
||||
|
||||
// Covers indexes on the first clustering column (among others).
|
||||
if (isKeyRange && hasQueriableClusteringColumnIndex)
|
||||
|
|
@ -157,10 +190,18 @@ public final class StatementRestrictions
|
|||
// there is restrictions not covered by the PK.
|
||||
if (!nonPrimaryKeyRestrictions.isEmpty())
|
||||
{
|
||||
if (!type.allowNonPrimaryKeyInWhereClause())
|
||||
{
|
||||
Collection<ColumnIdentifier> nonPrimaryKeyColumns =
|
||||
ColumnDefinition.toIdentifiers(nonPrimaryKeyRestrictions.getColumnDefs());
|
||||
|
||||
throw invalidRequest("Non PRIMARY KEY columns found in where clause: %s ",
|
||||
Joiner.on(", ").join(nonPrimaryKeyColumns));
|
||||
}
|
||||
if (hasQueriableIndex)
|
||||
usesSecondaryIndexing = true;
|
||||
else if (!useFiltering)
|
||||
throw new InvalidRequestException(NO_INDEX_FOUND_MESSAGE);
|
||||
throw invalidRequest(NO_INDEX_FOUND_MESSAGE);
|
||||
|
||||
indexRestrictions.add(nonPrimaryKeyRestrictions);
|
||||
}
|
||||
|
|
@ -169,7 +210,7 @@ public final class StatementRestrictions
|
|||
validateSecondaryIndexSelections(selectsOnlyStaticColumns);
|
||||
}
|
||||
|
||||
private void addRestriction(Restriction restriction) throws InvalidRequestException
|
||||
private void addRestriction(Restriction restriction)
|
||||
{
|
||||
if (restriction.isMultiColumn())
|
||||
clusteringColumnsRestrictions = clusteringColumnsRestrictions.mergeWith(restriction);
|
||||
|
|
@ -186,7 +227,7 @@ public final class StatementRestrictions
|
|||
nonPrimaryKeyRestrictions.getFunctions());
|
||||
}
|
||||
|
||||
private void addSingleColumnRestriction(SingleColumnRestriction restriction) throws InvalidRequestException
|
||||
private void addSingleColumnRestriction(SingleColumnRestriction restriction)
|
||||
{
|
||||
ColumnDefinition def = restriction.columnDef;
|
||||
if (def.isPartitionKey())
|
||||
|
|
@ -241,8 +282,19 @@ public final class StatementRestrictions
|
|||
return this.usesSecondaryIndexing;
|
||||
}
|
||||
|
||||
private void processPartitionKeyRestrictions(boolean hasQueriableIndex) throws InvalidRequestException
|
||||
private void processPartitionKeyRestrictions(boolean hasQueriableIndex)
|
||||
{
|
||||
if (!type.allowPartitionKeyRanges())
|
||||
{
|
||||
checkFalse(partitionKeyRestrictions.isOnToken(),
|
||||
"The token function cannot be used in WHERE clauses for %s statements", type);
|
||||
|
||||
if (hasUnrestrictedPartitionKeyComponents())
|
||||
throw invalidRequest("Some partition key parts are missing: %s",
|
||||
Joiner.on(", ").join(getPartitionKeyUnrestrictedComponents()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is a queriable index, no special condition are required on the other restrictions.
|
||||
// But we still need to know 2 things:
|
||||
// - If we don't have a queriable index, is the query ok
|
||||
|
|
@ -252,17 +304,18 @@ public final class StatementRestrictions
|
|||
if (partitionKeyRestrictions.isOnToken())
|
||||
isKeyRange = true;
|
||||
|
||||
if (hasPartitionKeyUnrestrictedComponents())
|
||||
{
|
||||
if (!partitionKeyRestrictions.isEmpty())
|
||||
if (hasUnrestrictedPartitionKeyComponents())
|
||||
{
|
||||
if (!hasQueriableIndex)
|
||||
throw invalidRequest("Partition key parts: %s must be restricted as other parts are",
|
||||
Joiner.on(", ").join(getPartitionKeyUnrestrictedComponents()));
|
||||
}
|
||||
if (!partitionKeyRestrictions.isEmpty())
|
||||
{
|
||||
if (!hasQueriableIndex)
|
||||
throw invalidRequest("Partition key parts: %s must be restricted as other parts are",
|
||||
Joiner.on(", ").join(getPartitionKeyUnrestrictedComponents()));
|
||||
}
|
||||
|
||||
isKeyRange = true;
|
||||
usesSecondaryIndexing = hasQueriableIndex;
|
||||
isKeyRange = true;
|
||||
usesSecondaryIndexing = hasQueriableIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +323,7 @@ public final class StatementRestrictions
|
|||
* Checks if the partition key has some unrestricted components.
|
||||
* @return <code>true</code> if the partition key has some unrestricted components, <code>false</code> otherwise.
|
||||
*/
|
||||
private boolean hasPartitionKeyUnrestrictedComponents()
|
||||
private boolean hasUnrestrictedPartitionKeyComponents()
|
||||
{
|
||||
return partitionKeyRestrictions.size() < cfm.partitionKeyColumns().size();
|
||||
}
|
||||
|
|
@ -284,47 +337,73 @@ public final class StatementRestrictions
|
|||
* Returns the partition key components that are not restricted.
|
||||
* @return the partition key components that are not restricted.
|
||||
*/
|
||||
private List<ColumnIdentifier> getPartitionKeyUnrestrictedComponents()
|
||||
private Collection<ColumnIdentifier> getPartitionKeyUnrestrictedComponents()
|
||||
{
|
||||
List<ColumnDefinition> list = new ArrayList<>(cfm.partitionKeyColumns());
|
||||
list.removeAll(partitionKeyRestrictions.getColumnDefs());
|
||||
return ColumnDefinition.toIdentifiers(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the restrictions on the partition key are token restrictions.
|
||||
*
|
||||
* @return <code>true</code> if the restrictions on the partition key are token restrictions,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isPartitionKeyRestrictionsOnToken()
|
||||
{
|
||||
return partitionKeyRestrictions.isOnToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the clustering column restrictions.
|
||||
*
|
||||
* @param hasQueriableIndex <code>true</code> if some of the queried data are indexed, <code>false</code> otherwise
|
||||
* @param selectsOnlyStaticColumns <code>true</code> if the selected or modified columns are all statics,
|
||||
* <code>false</code> otherwise.
|
||||
* @param selectACollection <code>true</code> if the query should return a collection column
|
||||
* @throws InvalidRequestException if the request is invalid
|
||||
*/
|
||||
private void processClusteringColumnsRestrictions(boolean hasQueriableIndex,
|
||||
boolean selectACollection) throws InvalidRequestException
|
||||
boolean selectsOnlyStaticColumns,
|
||||
boolean selectACollection)
|
||||
{
|
||||
checkFalse(clusteringColumnsRestrictions.isIN() && selectACollection,
|
||||
"Cannot restrict clustering columns by IN relations when a collection is selected by the query");
|
||||
checkFalse(clusteringColumnsRestrictions.isContains() && !hasQueriableIndex,
|
||||
"Cannot restrict clustering columns by a CONTAINS relation without a secondary index");
|
||||
checkFalse(!type.allowClusteringColumnSlices() && clusteringColumnsRestrictions.isSlice(),
|
||||
"Slice restrictions are not supported on the clustering columns in %s statements", type);
|
||||
|
||||
if (hasClusteringColumnsRestriction())
|
||||
if (!type.allowClusteringColumnSlices()
|
||||
&& (!cfm.isCompactTable() || (cfm.isCompactTable() && !hasClusteringColumnsRestriction())))
|
||||
{
|
||||
List<ColumnDefinition> clusteringColumns = cfm.clusteringColumns();
|
||||
List<ColumnDefinition> restrictedColumns = new LinkedList<>(clusteringColumnsRestrictions.getColumnDefs());
|
||||
if (!selectsOnlyStaticColumns && hasUnrestrictedClusteringColumns())
|
||||
throw invalidRequest("Some clustering keys are missing: %s",
|
||||
Joiner.on(", ").join(getUnrestrictedClusteringColumns()));
|
||||
}
|
||||
else
|
||||
{
|
||||
checkFalse(clusteringColumnsRestrictions.isIN() && selectACollection,
|
||||
"Cannot restrict clustering columns by IN relations when a collection is selected by the query");
|
||||
checkFalse(clusteringColumnsRestrictions.isContains() && !hasQueriableIndex,
|
||||
"Cannot restrict clustering columns by a CONTAINS relation without a secondary index");
|
||||
|
||||
for (int i = 0, m = restrictedColumns.size(); i < m; i++)
|
||||
if (hasClusteringColumnsRestriction())
|
||||
{
|
||||
ColumnDefinition clusteringColumn = clusteringColumns.get(i);
|
||||
ColumnDefinition restrictedColumn = restrictedColumns.get(i);
|
||||
List<ColumnDefinition> clusteringColumns = cfm.clusteringColumns();
|
||||
List<ColumnDefinition> restrictedColumns = new LinkedList<>(clusteringColumnsRestrictions.getColumnDefs());
|
||||
|
||||
if (!clusteringColumn.equals(restrictedColumn))
|
||||
for (int i = 0, m = restrictedColumns.size(); i < m; i++)
|
||||
{
|
||||
checkTrue(hasQueriableIndex,
|
||||
"PRIMARY KEY column \"%s\" cannot be restricted as preceding column \"%s\" is not restricted",
|
||||
restrictedColumn.name,
|
||||
clusteringColumn.name);
|
||||
ColumnDefinition clusteringColumn = clusteringColumns.get(i);
|
||||
ColumnDefinition restrictedColumn = restrictedColumns.get(i);
|
||||
|
||||
usesSecondaryIndexing = true; // handle gaps and non-keyrange cases.
|
||||
break;
|
||||
if (!clusteringColumn.equals(restrictedColumn))
|
||||
{
|
||||
checkTrue(hasQueriableIndex,
|
||||
"PRIMARY KEY column \"%s\" cannot be restricted as preceding column \"%s\" is not restricted",
|
||||
restrictedColumn.name,
|
||||
clusteringColumn.name);
|
||||
|
||||
usesSecondaryIndexing = true; // handle gaps and non-keyrange cases.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -333,7 +412,27 @@ public final class StatementRestrictions
|
|||
usesSecondaryIndexing = true;
|
||||
}
|
||||
|
||||
public RowFilter getRowFilter(SecondaryIndexManager indexManager, QueryOptions options) throws InvalidRequestException
|
||||
/**
|
||||
* Returns the clustering columns that are not restricted.
|
||||
* @return the clustering columns that are not restricted.
|
||||
*/
|
||||
private Collection<ColumnIdentifier> getUnrestrictedClusteringColumns()
|
||||
{
|
||||
List<ColumnDefinition> missingClusteringColumns = new ArrayList<>(cfm.clusteringColumns());
|
||||
missingClusteringColumns.removeAll(new LinkedList<>(clusteringColumnsRestrictions.getColumnDefs()));
|
||||
return ColumnDefinition.toIdentifiers(missingClusteringColumns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if some clustering columns are not restricted.
|
||||
* @return <code>true</code> if some clustering columns are not restricted, <code>false</code> otherwise.
|
||||
*/
|
||||
private boolean hasUnrestrictedClusteringColumns()
|
||||
{
|
||||
return cfm.clusteringColumns().size() != clusteringColumnsRestrictions.size();
|
||||
}
|
||||
|
||||
public RowFilter getRowFilter(SecondaryIndexManager indexManager, QueryOptions options)
|
||||
{
|
||||
if (indexRestrictions.isEmpty())
|
||||
return RowFilter.NONE;
|
||||
|
|
@ -350,9 +449,8 @@ public final class StatementRestrictions
|
|||
*
|
||||
* @param options the query options
|
||||
* @return the partition keys for which the data is requested.
|
||||
* @throws InvalidRequestException if the partition keys cannot be retrieved
|
||||
*/
|
||||
public Collection<ByteBuffer> getPartitionKeys(final QueryOptions options) throws InvalidRequestException
|
||||
public List<ByteBuffer> getPartitionKeys(final QueryOptions options)
|
||||
{
|
||||
return partitionKeyRestrictions.values(options);
|
||||
}
|
||||
|
|
@ -363,13 +461,12 @@ public final class StatementRestrictions
|
|||
* @param b the boundary type
|
||||
* @param options the query options
|
||||
* @return the specified bound of the partition key
|
||||
* @throws InvalidRequestException if the boundary cannot be retrieved
|
||||
*/
|
||||
private ByteBuffer getPartitionKeyBound(Bound b, QueryOptions options) throws InvalidRequestException
|
||||
private ByteBuffer getPartitionKeyBound(Bound b, QueryOptions options)
|
||||
{
|
||||
// Deal with unrestricted partition key components (special-casing is required to deal with 2i queries on the
|
||||
// first component of a composite partition key).
|
||||
if (hasPartitionKeyUnrestrictedComponents())
|
||||
if (hasUnrestrictedPartitionKeyComponents())
|
||||
return ByteBufferUtil.EMPTY_BYTE_BUFFER;
|
||||
|
||||
// We deal with IN queries for keys in other places, so we know buildBound will return only one result
|
||||
|
|
@ -381,9 +478,8 @@ public final class StatementRestrictions
|
|||
*
|
||||
* @param options the query options
|
||||
* @return the partition key bounds
|
||||
* @throws InvalidRequestException if the query is invalid
|
||||
*/
|
||||
public AbstractBounds<PartitionPosition> getPartitionKeyBounds(QueryOptions options) throws InvalidRequestException
|
||||
public AbstractBounds<PartitionPosition> getPartitionKeyBounds(QueryOptions options)
|
||||
{
|
||||
IPartitioner p = cfm.partitioner;
|
||||
|
||||
|
|
@ -396,7 +492,7 @@ public final class StatementRestrictions
|
|||
}
|
||||
|
||||
private AbstractBounds<PartitionPosition> getPartitionKeyBounds(IPartitioner p,
|
||||
QueryOptions options) throws InvalidRequestException
|
||||
QueryOptions options)
|
||||
{
|
||||
ByteBuffer startKeyBytes = getPartitionKeyBound(Bound.START, options);
|
||||
ByteBuffer finishKeyBytes = getPartitionKeyBound(Bound.END, options);
|
||||
|
|
@ -420,8 +516,7 @@ public final class StatementRestrictions
|
|||
}
|
||||
|
||||
private AbstractBounds<PartitionPosition> getPartitionKeyBoundsForTokenRestrictions(IPartitioner p,
|
||||
QueryOptions options)
|
||||
throws InvalidRequestException
|
||||
QueryOptions options)
|
||||
{
|
||||
Token startToken = getTokenBound(Bound.START, options, p);
|
||||
Token endToken = getTokenBound(Bound.END, options, p);
|
||||
|
|
@ -450,7 +545,7 @@ public final class StatementRestrictions
|
|||
return new Range<>(start, end);
|
||||
}
|
||||
|
||||
private Token getTokenBound(Bound b, QueryOptions options, IPartitioner p) throws InvalidRequestException
|
||||
private Token getTokenBound(Bound b, QueryOptions options, IPartitioner p)
|
||||
{
|
||||
if (!partitionKeyRestrictions.hasBound(b))
|
||||
return p.getMinimumToken();
|
||||
|
|
@ -476,9 +571,8 @@ public final class StatementRestrictions
|
|||
*
|
||||
* @param options the query options
|
||||
* @return the requested clustering columns
|
||||
* @throws InvalidRequestException if the query is not valid
|
||||
*/
|
||||
public NavigableSet<Clustering> getClusteringColumns(QueryOptions options) throws InvalidRequestException
|
||||
public NavigableSet<Clustering> getClusteringColumns(QueryOptions options)
|
||||
{
|
||||
// If this is a names command and the table is a static compact one, then as far as CQL is concerned we have
|
||||
// only a single row which internally correspond to the static parts. In which case we want to return an empty
|
||||
|
|
@ -495,9 +589,8 @@ public final class StatementRestrictions
|
|||
* @param b the bound type
|
||||
* @param options the query options
|
||||
* @return the bounds (start or end) of the clustering columns
|
||||
* @throws InvalidRequestException if the request is not valid
|
||||
*/
|
||||
public NavigableSet<Slice.Bound> getClusteringColumnsBounds(Bound b, QueryOptions options) throws InvalidRequestException
|
||||
public NavigableSet<Slice.Bound> getClusteringColumnsBounds(Bound b, QueryOptions options)
|
||||
{
|
||||
return clusteringColumnsRestrictions.boundsAsClustering(b, options);
|
||||
}
|
||||
|
|
@ -546,7 +639,7 @@ public final class StatementRestrictions
|
|||
&& nonPrimaryKeyRestrictions.hasMultipleContains());
|
||||
}
|
||||
|
||||
private void validateSecondaryIndexSelections(boolean selectsOnlyStaticColumns) throws InvalidRequestException
|
||||
private void validateSecondaryIndexSelections(boolean selectsOnlyStaticColumns)
|
||||
{
|
||||
checkFalse(keyIsInRelation(),
|
||||
"Select on indexed columns and with IN clause for the PRIMARY KEY are not supported");
|
||||
|
|
@ -556,4 +649,19 @@ public final class StatementRestrictions
|
|||
// so far, 2i means that you've restricted a non static column, so the query is somewhat non-sensical.
|
||||
checkFalse(selectsOnlyStaticColumns, "Queries using 2ndary indexes don't support selecting only static columns");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that all the primary key columns (partition key and clustering columns) are restricted by an equality
|
||||
* relation ('=' or 'IN').
|
||||
*
|
||||
* @return <code>true</code> if all the primary key columns are restricted by an equality relation.
|
||||
*/
|
||||
public boolean hasAllPKColumnsRestrictedByEqualities()
|
||||
{
|
||||
return !isPartitionKeyRestrictionsOnToken()
|
||||
&& !hasUnrestrictedPartitionKeyComponents()
|
||||
&& (partitionKeyRestrictions.isEQ() || partitionKeyRestrictions.isIN())
|
||||
&& !hasUnrestrictedClusteringColumns()
|
||||
&& (clusteringColumnsRestrictions.isEQ() || clusteringColumnsRestrictions.isIN());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import com.google.common.collect.Iterables;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.*;
|
||||
|
|
@ -44,6 +43,8 @@ import org.apache.cassandra.transport.messages.ResultMessage;
|
|||
import org.apache.cassandra.utils.NoSpamLogger;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
||||
|
||||
/**
|
||||
* A <code>BATCH</code> statement parsed from a CQL query.
|
||||
*/
|
||||
|
|
@ -217,8 +218,7 @@ public class BatchStatement implements CQLStatement
|
|||
throws RequestExecutionException, RequestValidationException
|
||||
{
|
||||
Set<String> tablesWithZeroGcGs = null;
|
||||
|
||||
Map<String, Map<ByteBuffer, IMutation>> mutations = new HashMap<>();
|
||||
UpdatesCollector collector = new UpdatesCollector(updatedColumns, updatedRows());
|
||||
for (int i = 0; i < statements.size(); i++)
|
||||
{
|
||||
ModificationStatement statement = statements.get(i);
|
||||
|
|
@ -230,7 +230,7 @@ public class BatchStatement implements CQLStatement
|
|||
}
|
||||
QueryOptions statementOptions = options.forStatement(i);
|
||||
long timestamp = attrs.getTimestamp(now, statementOptions);
|
||||
addStatementMutations(statement, statementOptions, local, timestamp, mutations);
|
||||
statement.addUpdates(collector, statementOptions, local, timestamp);
|
||||
}
|
||||
|
||||
if (tablesWithZeroGcGs != null)
|
||||
|
|
@ -242,27 +242,7 @@ public class BatchStatement implements CQLStatement
|
|||
.getMessage());
|
||||
}
|
||||
|
||||
return unzipMutations(mutations);
|
||||
}
|
||||
|
||||
private Collection<? extends IMutation> unzipMutations(Map<String, Map<ByteBuffer, IMutation>> mutations)
|
||||
{
|
||||
|
||||
// The case where all statement where on the same keyspace is pretty common
|
||||
if (mutations.size() == 1)
|
||||
return mutations.values().iterator().next().values();
|
||||
|
||||
|
||||
List<IMutation> ms = new ArrayList<>();
|
||||
for (Map<ByteBuffer, IMutation> ksMap : mutations.values())
|
||||
ms.addAll(ksMap.values());
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
private PartitionColumns updatedColumns()
|
||||
{
|
||||
return updatedColumns;
|
||||
return collector.toMutations();
|
||||
}
|
||||
|
||||
private int updatedRows()
|
||||
|
|
@ -272,55 +252,6 @@ public class BatchStatement implements CQLStatement
|
|||
return statements.size();
|
||||
}
|
||||
|
||||
private void addStatementMutations(ModificationStatement statement,
|
||||
QueryOptions options,
|
||||
boolean local,
|
||||
long now,
|
||||
Map<String, Map<ByteBuffer, IMutation>> mutations)
|
||||
throws RequestExecutionException, RequestValidationException
|
||||
{
|
||||
String ksName = statement.keyspace();
|
||||
Map<ByteBuffer, IMutation> ksMap = mutations.get(ksName);
|
||||
if (ksMap == null)
|
||||
{
|
||||
ksMap = new HashMap<>();
|
||||
mutations.put(ksName, ksMap);
|
||||
}
|
||||
|
||||
// The following does the same than statement.getMutations(), but we inline it here because
|
||||
// we don't want to recreate mutations every time as this is particularly inefficient when applying
|
||||
// multiple batch to the same partition (see #6737).
|
||||
List<ByteBuffer> keys = statement.buildPartitionKeyNames(options);
|
||||
CBuilder clustering = statement.createClustering(options);
|
||||
UpdateParameters params = statement.makeUpdateParameters(keys, clustering, options, local, now);
|
||||
|
||||
for (ByteBuffer key : keys)
|
||||
{
|
||||
DecoratedKey dk = statement.cfm.decorateKey(key);
|
||||
IMutation mutation = ksMap.get(dk.getKey());
|
||||
Mutation mut;
|
||||
if (mutation == null)
|
||||
{
|
||||
mut = new Mutation(ksName, dk);
|
||||
mutation = statement.cfm.isCounter() ? new CounterMutation(mut, options.getConsistency()) : mut;
|
||||
ksMap.put(dk.getKey(), mutation);
|
||||
}
|
||||
else
|
||||
{
|
||||
mut = statement.cfm.isCounter() ? ((CounterMutation) mutation).getMutation() : (Mutation) mutation;
|
||||
}
|
||||
|
||||
PartitionUpdate upd = mut.get(statement.cfm);
|
||||
if (upd == null)
|
||||
{
|
||||
upd = new PartitionUpdate(statement.cfm, dk, updatedColumns(), updatedRows());
|
||||
mut.add(upd);
|
||||
}
|
||||
|
||||
statement.addUpdateForKey(upd, clustering, params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks batch size to ensure threshold is met. If not, a warning is logged.
|
||||
*
|
||||
|
|
@ -470,17 +401,23 @@ public class BatchStatement implements CQLStatement
|
|||
throw new InvalidRequestException("Batch with conditions cannot span multiple partitions");
|
||||
}
|
||||
|
||||
CBuilder cbuilder = statement.createClustering(statementOptions);
|
||||
SortedSet<Clustering> clusterings = statement.createClustering(statementOptions);
|
||||
|
||||
checkFalse(clusterings.size() > 1,
|
||||
"IN on the clustering key columns is not supported with conditional updates");
|
||||
|
||||
Clustering clustering = Iterables.getOnlyElement(clusterings);
|
||||
|
||||
if (statement.hasConditions())
|
||||
{
|
||||
statement.addConditions(cbuilder.build(), casRequest, statementOptions);
|
||||
statement.addConditions(clustering, casRequest, statementOptions);
|
||||
// As soon as we have a ifNotExists, we set columnsWithConditions to null so that everything is in the resultSet
|
||||
if (statement.hasIfNotExistCondition() || statement.hasIfExistCondition())
|
||||
columnsWithConditions = null;
|
||||
else if (columnsWithConditions != null)
|
||||
Iterables.addAll(columnsWithConditions, statement.getColumnsWithConditions());
|
||||
}
|
||||
casRequest.addRowUpdate(cbuilder, statement, statementOptions, timestamp);
|
||||
casRequest.addRowUpdate(clustering, statement, statementOptions, timestamp);
|
||||
}
|
||||
|
||||
return Pair.create(casRequest, columnsWithConditions);
|
||||
|
|
|
|||
|
|
@ -67,9 +67,9 @@ public class CQL3CasRequest implements CASRequest
|
|||
this.updatesStaticRow = updatesStaticRow;
|
||||
}
|
||||
|
||||
public void addRowUpdate(CBuilder cbuilder, ModificationStatement stmt, QueryOptions options, long timestamp)
|
||||
public void addRowUpdate(Clustering clustering, ModificationStatement stmt, QueryOptions options, long timestamp)
|
||||
{
|
||||
updates.add(new RowUpdate(cbuilder, stmt, options, timestamp));
|
||||
updates.add(new RowUpdate(clustering, stmt, options, timestamp));
|
||||
}
|
||||
|
||||
public void addNotExist(Clustering clustering) throws InvalidRequestException
|
||||
|
|
@ -129,7 +129,7 @@ public class CQL3CasRequest implements CASRequest
|
|||
return conditionColumns;
|
||||
}
|
||||
|
||||
public SinglePartitionReadCommand readCommand(int nowInSec)
|
||||
public SinglePartitionReadCommand<?> readCommand(int nowInSec)
|
||||
{
|
||||
assert !conditions.isEmpty();
|
||||
Slices.Builder builder = new Slices.Builder(cfm.comparator, conditions.size());
|
||||
|
|
@ -184,14 +184,14 @@ public class CQL3CasRequest implements CASRequest
|
|||
*/
|
||||
private class RowUpdate
|
||||
{
|
||||
private final CBuilder cbuilder;
|
||||
private final Clustering clustering;
|
||||
private final ModificationStatement stmt;
|
||||
private final QueryOptions options;
|
||||
private final long timestamp;
|
||||
|
||||
private RowUpdate(CBuilder cbuilder, ModificationStatement stmt, QueryOptions options, long timestamp)
|
||||
private RowUpdate(Clustering clustering, ModificationStatement stmt, QueryOptions options, long timestamp)
|
||||
{
|
||||
this.cbuilder = cbuilder;
|
||||
this.clustering = clustering;
|
||||
this.stmt = stmt;
|
||||
this.options = options;
|
||||
this.timestamp = timestamp;
|
||||
|
|
@ -201,7 +201,7 @@ public class CQL3CasRequest implements CASRequest
|
|||
{
|
||||
Map<DecoratedKey, Partition> map = stmt.requiresRead() ? Collections.<DecoratedKey, Partition>singletonMap(key, current) : null;
|
||||
UpdateParameters params = new UpdateParameters(cfm, updates.columns(), options, timestamp, stmt.getTimeToLive(options), map, true);
|
||||
stmt.addUpdateForKey(updates, cbuilder, params);
|
||||
stmt.addUpdateForKey(updates, clustering, params);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,37 +17,38 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.statements;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
|
||||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.cql3.*;
|
||||
import org.apache.cassandra.cql3.restrictions.Restriction;
|
||||
import org.apache.cassandra.db.CBuilder;
|
||||
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
|
||||
import org.apache.cassandra.db.Clustering;
|
||||
import org.apache.cassandra.db.Slice;
|
||||
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||
|
||||
/**
|
||||
* A <code>DELETE</code> parsed from a CQL query statement.
|
||||
*/
|
||||
public class DeleteStatement extends ModificationStatement
|
||||
{
|
||||
private DeleteStatement(StatementType type, int boundTerms, CFMetaData cfm, Attributes attrs)
|
||||
private DeleteStatement(int boundTerms,
|
||||
CFMetaData cfm,
|
||||
Operations operations,
|
||||
StatementRestrictions restrictions,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
{
|
||||
super(type, boundTerms, cfm, attrs);
|
||||
super(StatementType.DELETE, boundTerms, cfm, operations, restrictions, conditions, attrs);
|
||||
}
|
||||
|
||||
public boolean requireFullClusteringKey()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addUpdateForKey(PartitionUpdate update, CBuilder cbuilder, UpdateParameters params)
|
||||
@Override
|
||||
public void addUpdateForKey(PartitionUpdate update, Clustering clustering, UpdateParameters params)
|
||||
throws InvalidRequestException
|
||||
{
|
||||
List<Operation> regularDeletions = getRegularOperations();
|
||||
|
|
@ -56,32 +57,29 @@ public class DeleteStatement extends ModificationStatement
|
|||
if (regularDeletions.isEmpty() && staticDeletions.isEmpty())
|
||||
{
|
||||
// We're not deleting any specific columns so it's either a full partition deletion ....
|
||||
if (cbuilder.count() == 0)
|
||||
if (clustering.size() == 0)
|
||||
{
|
||||
update.addPartitionDeletion(params.deletionTime());
|
||||
}
|
||||
// ... or a row deletion ...
|
||||
else if (cbuilder.remainingCount() == 0)
|
||||
else if (clustering.size() == cfm.clusteringColumns().size())
|
||||
{
|
||||
params.newRow(cbuilder.build());
|
||||
params.newRow(clustering);
|
||||
params.addRowDeletion();
|
||||
update.add(params.buildRow());
|
||||
}
|
||||
// ... or a range of rows deletion.
|
||||
else
|
||||
{
|
||||
update.add(params.makeRangeTombstone(cbuilder));
|
||||
update.add(params.makeRangeTombstone(cfm.comparator, clustering));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!regularDeletions.isEmpty())
|
||||
{
|
||||
// We can't delete specific (regular) columns if not all clustering columns have been specified.
|
||||
if (cbuilder.remainingCount() > 0)
|
||||
throw new InvalidRequestException(String.format("Primary key column '%s' must be specified in order to delete column '%s'", getFirstEmptyKey().name, regularDeletions.get(0).column.name));
|
||||
params.newRow(clustering);
|
||||
|
||||
params.newRow(cbuilder.build());
|
||||
for (Operation op : regularDeletions)
|
||||
op.execute(update.partitionKey(), params);
|
||||
update.add(params.buildRow());
|
||||
|
|
@ -99,21 +97,16 @@ public class DeleteStatement extends ModificationStatement
|
|||
params.validateIndexedColumns(update);
|
||||
}
|
||||
|
||||
protected void validateWhereClauseForConditions() throws InvalidRequestException
|
||||
@Override
|
||||
public void addUpdateForKey(PartitionUpdate update, Slice slice, UpdateParameters params)
|
||||
{
|
||||
Iterator<ColumnDefinition> iterator = Iterators.concat(cfm.partitionKeyColumns().iterator(), cfm.clusteringColumns().iterator());
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
ColumnDefinition def = iterator.next();
|
||||
Restriction restriction = processedKeys.get(def.name);
|
||||
if (restriction == null || !(restriction.isEQ() || restriction.isIN()))
|
||||
{
|
||||
throw new InvalidRequestException(
|
||||
String.format("DELETE statements must restrict all PRIMARY KEY columns with equality relations in order " +
|
||||
"to use IF conditions, but column '%s' is not restricted", def.name));
|
||||
}
|
||||
}
|
||||
List<Operation> regularDeletions = getRegularOperations();
|
||||
List<Operation> staticDeletions = getStaticOperations();
|
||||
|
||||
checkTrue(regularDeletions.isEmpty() && staticDeletions.isEmpty(),
|
||||
"Range deletions are not supported for specific columns");
|
||||
|
||||
update.add(params.makeRangeTombstone(slice));
|
||||
}
|
||||
|
||||
public static class Parsed extends ModificationStatement.Parsed
|
||||
|
|
@ -133,28 +126,46 @@ public class DeleteStatement extends ModificationStatement
|
|||
this.whereClause = whereClause;
|
||||
}
|
||||
|
||||
protected ModificationStatement prepareInternal(CFMetaData cfm, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException
|
||||
|
||||
@Override
|
||||
protected ModificationStatement prepareInternal(CFMetaData cfm,
|
||||
VariableSpecifications boundNames,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
{
|
||||
DeleteStatement stmt = new DeleteStatement(ModificationStatement.StatementType.DELETE, boundNames.size(), cfm, attrs);
|
||||
Operations operations = new Operations();
|
||||
|
||||
for (Operation.RawDeletion deletion : deletions)
|
||||
{
|
||||
ColumnIdentifier id = deletion.affectedColumn().prepare(cfm);
|
||||
ColumnDefinition def = cfm.getColumnDefinition(id);
|
||||
if (def == null)
|
||||
throw new InvalidRequestException(String.format("Unknown identifier %s", id));
|
||||
ColumnDefinition def = getColumnDefinition(cfm, deletion.affectedColumn());
|
||||
|
||||
// For compact, we only have one value except the key, so the only form of DELETE that make sense is without a column
|
||||
// list. However, we support having the value name for coherence with the static/sparse case
|
||||
if (def.isPrimaryKeyColumn())
|
||||
throw new InvalidRequestException(String.format("Invalid identifier %s for deletion (should not be a PRIMARY KEY part)", def.name));
|
||||
checkFalse(def.isPrimaryKeyColumn(), "Invalid identifier %s for deletion (should not be a PRIMARY KEY part)", def.name);
|
||||
|
||||
Operation op = deletion.prepare(cfm.ksName, def);
|
||||
op.collectMarkerSpecification(boundNames);
|
||||
stmt.addOperation(op);
|
||||
operations.add(op);
|
||||
}
|
||||
|
||||
stmt.processWhereClause(whereClause, boundNames);
|
||||
StatementRestrictions restrictions = newRestrictions(StatementType.DELETE,
|
||||
cfm,
|
||||
boundNames,
|
||||
operations,
|
||||
whereClause,
|
||||
conditions);
|
||||
|
||||
DeleteStatement stmt = new DeleteStatement(boundNames.size(),
|
||||
cfm,
|
||||
operations,
|
||||
restrictions,
|
||||
conditions,
|
||||
attrs);
|
||||
|
||||
if (stmt.hasConditions())
|
||||
checkTrue(restrictions.hasAllPKColumnsRestrictedByEqualities(),
|
||||
"DELETE statements must restrict all PRIMARY KEY columns with equality relations" +
|
||||
" in order to use IF conditions");
|
||||
return stmt;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -23,9 +23,10 @@ import java.util.*;
|
|||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
|
|
@ -57,7 +58,6 @@ import org.apache.cassandra.thrift.ThriftValidation;
|
|||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||
|
|
@ -156,7 +156,7 @@ public class SelectStatement implements CQLStatement
|
|||
0,
|
||||
defaultParameters,
|
||||
selection,
|
||||
StatementRestrictions.empty(cfm),
|
||||
StatementRestrictions.empty(StatementType.SELECT, cfm),
|
||||
false,
|
||||
null,
|
||||
null);
|
||||
|
|
@ -790,7 +790,8 @@ public class SelectStatement implements CQLStatement
|
|||
{
|
||||
try
|
||||
{
|
||||
return new StatementRestrictions(cfm,
|
||||
return new StatementRestrictions(StatementType.SELECT,
|
||||
cfm,
|
||||
whereClause,
|
||||
boundNames,
|
||||
selection.containsOnlyStaticColumns(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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.statements;
|
||||
|
||||
public enum StatementType
|
||||
{
|
||||
INSERT
|
||||
{
|
||||
@Override
|
||||
public boolean allowClusteringColumnSlices()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
},
|
||||
UPDATE
|
||||
{
|
||||
|
||||
@Override
|
||||
public boolean allowClusteringColumnSlices()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
},
|
||||
DELETE
|
||||
{
|
||||
},
|
||||
SELECT
|
||||
{
|
||||
@Override
|
||||
public boolean allowPartitionKeyRanges()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowNonPrimaryKeyInWhereClause()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowUseOfSecondaryIndices()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if this type is an insert.
|
||||
* @return <code>true</code> if this type is an insert, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isInsert()
|
||||
{
|
||||
return this == INSERT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this type is an update.
|
||||
* @return <code>true</code> if this type is an update, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isUpdate()
|
||||
{
|
||||
return this == UPDATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this type is a delete.
|
||||
* @return <code>true</code> if this type is a delete, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isDelete()
|
||||
{
|
||||
return this == DELETE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this type is a select.
|
||||
* @return <code>true</code> if this type is a select, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isSelect()
|
||||
{
|
||||
return this == SELECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks this statement allow the where clause to contains missing partition key components or token relation.
|
||||
* @return <code>true</code> if this statement allow the where clause to contains missing partition key components
|
||||
* or token relation, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean allowPartitionKeyRanges()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks this type of statement allow the where clause to contains clustering column slices.
|
||||
* @return <code>true</code> if this type of statement allow the where clause to contains clustering column slices,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean allowClusteringColumnSlices()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this type of statement allow non primary key in the where clause.
|
||||
* @return <code>true</code> if this type of statement allow non primary key in the where clause,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean allowNonPrimaryKeyInWhereClause()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this type of statement allow the use of secondary indices.
|
||||
* @return <code>true</code> if this type of statement allow the use of secondary indices,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean allowUseOfSecondaryIndices()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.statements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
@ -24,14 +25,18 @@ import java.util.List;
|
|||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.cql3.*;
|
||||
import org.apache.cassandra.db.CBuilder;
|
||||
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
|
||||
import org.apache.cassandra.db.Clustering;
|
||||
import org.apache.cassandra.db.CompactTables;
|
||||
import org.apache.cassandra.db.Slice;
|
||||
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkContainsNoDuplicates;
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||
|
||||
/**
|
||||
* An <code>UPDATE</code> statement parsed from a CQL query statement.
|
||||
*
|
||||
|
|
@ -40,9 +45,15 @@ public class UpdateStatement extends ModificationStatement
|
|||
{
|
||||
private static final Constants.Value EMPTY = new Constants.Value(ByteBufferUtil.EMPTY_BYTE_BUFFER);
|
||||
|
||||
private UpdateStatement(StatementType type, int boundTerms, CFMetaData cfm, Attributes attrs)
|
||||
private UpdateStatement(StatementType type,
|
||||
int boundTerms,
|
||||
CFMetaData cfm,
|
||||
Operations operations,
|
||||
StatementRestrictions restrictions,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
{
|
||||
super(type, boundTerms, cfm, attrs);
|
||||
super(type, boundTerms, cfm, operations, restrictions, conditions, attrs);
|
||||
}
|
||||
|
||||
public boolean requireFullClusteringKey()
|
||||
|
|
@ -50,22 +61,22 @@ public class UpdateStatement extends ModificationStatement
|
|||
return true;
|
||||
}
|
||||
|
||||
public void addUpdateForKey(PartitionUpdate update, CBuilder cbuilder, UpdateParameters params)
|
||||
throws InvalidRequestException
|
||||
@Override
|
||||
public void addUpdateForKey(PartitionUpdate update, Clustering clustering, UpdateParameters params)
|
||||
{
|
||||
if (updatesRegularRows())
|
||||
{
|
||||
params.newRow(cbuilder.build());
|
||||
params.newRow(clustering);
|
||||
|
||||
// We update the row timestamp (ex-row marker) only on INSERT (#6782)
|
||||
// Further, COMPACT tables semantic differs from "CQL3" ones in that a row exists only if it has
|
||||
// a non-null column, so we don't want to set the row timestamp for them.
|
||||
if (type == StatementType.INSERT && cfm.isCQLTable())
|
||||
if (type.isInsert() && cfm.isCQLTable())
|
||||
params.addPrimaryKeyLivenessInfo();
|
||||
|
||||
List<Operation> updates = getRegularOperations();
|
||||
|
||||
// For compact tablw, when we translate it to thrift, we don't have a row marker. So we don't accept an insert/update
|
||||
// For compact table, when we translate it to thrift, we don't have a row marker. So we don't accept an insert/update
|
||||
// that only sets the PK unless the is no declared non-PK columns (in the latter we just set the value empty).
|
||||
|
||||
// For a dense layout, when we translate it to thrift, we don't have a row marker. So we don't accept an insert/update
|
||||
|
|
@ -73,10 +84,11 @@ public class UpdateStatement extends ModificationStatement
|
|||
// value is of type "EmptyType").
|
||||
if (cfm.isCompactTable() && updates.isEmpty())
|
||||
{
|
||||
if (CompactTables.hasEmptyCompactValue(cfm))
|
||||
updates = Collections.<Operation>singletonList(new Constants.Setter(cfm.compactValueColumn(), EMPTY));
|
||||
else
|
||||
throw new InvalidRequestException(String.format("Column %s is mandatory for this COMPACT STORAGE table", cfm.compactValueColumn().name));
|
||||
checkTrue(CompactTables.hasEmptyCompactValue(cfm),
|
||||
"Column %s is mandatory for this COMPACT STORAGE table",
|
||||
cfm.compactValueColumn().name);
|
||||
|
||||
updates = Collections.<Operation>singletonList(new Constants.Setter(cfm.compactValueColumn(), EMPTY));
|
||||
}
|
||||
|
||||
for (Operation op : updates)
|
||||
|
|
@ -96,6 +108,12 @@ public class UpdateStatement extends ModificationStatement
|
|||
params.validateIndexedColumns(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUpdateForKey(PartitionUpdate update, Slice slice, UpdateParameters params)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static class ParsedInsert extends ModificationStatement.Parsed
|
||||
{
|
||||
private final List<ColumnIdentifier.Raw> columnNames;
|
||||
|
|
@ -121,52 +139,63 @@ public class UpdateStatement extends ModificationStatement
|
|||
this.columnValues = columnValues;
|
||||
}
|
||||
|
||||
protected ModificationStatement prepareInternal(CFMetaData cfm, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException
|
||||
@Override
|
||||
protected ModificationStatement prepareInternal(CFMetaData cfm,
|
||||
VariableSpecifications boundNames,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
{
|
||||
UpdateStatement stmt = new UpdateStatement(ModificationStatement.StatementType.INSERT, boundNames.size(), cfm, attrs);
|
||||
|
||||
// Created from an INSERT
|
||||
if (stmt.isCounter())
|
||||
throw new InvalidRequestException("INSERT statements are not allowed on counter tables, use UPDATE instead");
|
||||
checkFalse(cfm.isCounter(), "INSERT statements are not allowed on counter tables, use UPDATE instead");
|
||||
|
||||
if (columnNames == null)
|
||||
throw new InvalidRequestException("Column names for INSERT must be provided when using VALUES");
|
||||
if (columnNames.isEmpty())
|
||||
throw new InvalidRequestException("No columns provided to INSERT");
|
||||
if (columnNames.size() != columnValues.size())
|
||||
throw new InvalidRequestException("Unmatched column names/values");
|
||||
checkFalse(columnNames == null, "Column names for INSERT must be provided when using VALUES");
|
||||
checkFalse(columnNames.isEmpty(), "No columns provided to INSERT");
|
||||
checkFalse(columnNames.size() != columnValues.size(), "Unmatched column names/values");
|
||||
checkContainsNoDuplicates(columnNames, "The column names contains duplicates");
|
||||
|
||||
List<Relation> relations = new ArrayList<>();
|
||||
Operations operations = new Operations();
|
||||
boolean hasClusteringColumnsSet = false;
|
||||
|
||||
String ks = keyspace();
|
||||
for (int i = 0; i < columnNames.size(); i++)
|
||||
{
|
||||
ColumnIdentifier id = columnNames.get(i).prepare(cfm);
|
||||
ColumnDefinition def = cfm.getColumnDefinition(id);
|
||||
if (def == null)
|
||||
throw new InvalidRequestException(String.format("Unknown identifier %s", id));
|
||||
ColumnDefinition def = getColumnDefinition(cfm, columnNames.get(i));
|
||||
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
ColumnIdentifier otherId = columnNames.get(j).prepare(cfm);
|
||||
if (id.equals(otherId))
|
||||
throw new InvalidRequestException(String.format("Multiple definitions found for column %s", id));
|
||||
}
|
||||
if (def.isClusteringColumn())
|
||||
hasClusteringColumnsSet = true;
|
||||
|
||||
Term.Raw value = columnValues.get(i);
|
||||
|
||||
if (def.isPrimaryKeyColumn())
|
||||
{
|
||||
Term t = value.prepare(ks, def);
|
||||
t.collectMarkerSpecification(boundNames);
|
||||
stmt.addKeyValue(def, t);
|
||||
relations.add(new SingleColumnRelation(columnNames.get(i), Operator.EQ, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
Operation operation = new Operation.SetValue(value).prepare(ks, def);
|
||||
Operation operation = new Operation.SetValue(value).prepare(keyspace(), def);
|
||||
operation.collectMarkerSpecification(boundNames);
|
||||
stmt.addOperation(operation);
|
||||
operations.add(operation);
|
||||
}
|
||||
}
|
||||
|
||||
return stmt;
|
||||
boolean applyOnlyToStaticColumns = appliesOnlyToStaticColumns(operations, conditions) && !hasClusteringColumnsSet;
|
||||
|
||||
StatementRestrictions restrictions = new StatementRestrictions(StatementType.INSERT,
|
||||
cfm,
|
||||
relations,
|
||||
boundNames,
|
||||
applyOnlyToStaticColumns,
|
||||
false,
|
||||
false);
|
||||
|
||||
return new UpdateStatement(StatementType.INSERT,
|
||||
boundNames.size(),
|
||||
cfm,
|
||||
operations,
|
||||
restrictions,
|
||||
conditions,
|
||||
attrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,24 +212,58 @@ public class UpdateStatement extends ModificationStatement
|
|||
this.jsonValue = jsonValue;
|
||||
}
|
||||
|
||||
protected ModificationStatement prepareInternal(CFMetaData cfm, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException
|
||||
@Override
|
||||
protected ModificationStatement prepareInternal(CFMetaData cfm,
|
||||
VariableSpecifications boundNames,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
{
|
||||
UpdateStatement stmt = new UpdateStatement(ModificationStatement.StatementType.INSERT, boundNames.size(), cfm, attrs);
|
||||
if (stmt.isCounter())
|
||||
throw new InvalidRequestException("INSERT statements are not allowed on counter tables, use UPDATE instead");
|
||||
checkFalse(cfm.isCounter(), "INSERT statements are not allowed on counter tables, use UPDATE instead");
|
||||
|
||||
Collection<ColumnDefinition> defs = cfm.allColumns();
|
||||
Json.Prepared prepared = jsonValue.prepareAndCollectMarkers(cfm, defs, boundNames);
|
||||
|
||||
List<Relation> relations = new ArrayList<>();
|
||||
Operations operations = new Operations();
|
||||
boolean hasClusteringColumnsSet = false;
|
||||
|
||||
for (ColumnDefinition def : defs)
|
||||
{
|
||||
if (def.isClusteringColumn())
|
||||
hasClusteringColumnsSet = true;
|
||||
|
||||
Term.Raw raw = prepared.getRawTermForColumn(def);
|
||||
if (def.isPrimaryKeyColumn())
|
||||
stmt.addKeyValue(def, prepared.getPrimaryKeyValueForColumn(def));
|
||||
{
|
||||
relations.add(new SingleColumnRelation(new ColumnIdentifier.ColumnIdentifierValue(def.name),
|
||||
Operator.EQ,
|
||||
raw));
|
||||
}
|
||||
else
|
||||
stmt.addOperation(prepared.getSetOperationForColumn(def));
|
||||
{
|
||||
Operation operation = new Operation.SetValue(raw).prepare(keyspace(), def);
|
||||
operation.collectMarkerSpecification(boundNames);
|
||||
operations.add(operation);
|
||||
}
|
||||
}
|
||||
|
||||
return stmt;
|
||||
boolean applyOnlyToStaticColumns = appliesOnlyToStaticColumns(operations, conditions) && !hasClusteringColumnsSet;
|
||||
|
||||
StatementRestrictions restrictions = new StatementRestrictions(StatementType.INSERT,
|
||||
cfm,
|
||||
relations,
|
||||
boundNames,
|
||||
applyOnlyToStaticColumns,
|
||||
false,
|
||||
false);
|
||||
|
||||
return new UpdateStatement(StatementType.INSERT,
|
||||
boundNames.size(),
|
||||
cfm,
|
||||
operations,
|
||||
restrictions,
|
||||
conditions,
|
||||
attrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,32 +295,39 @@ public class UpdateStatement extends ModificationStatement
|
|||
this.whereClause = whereClause;
|
||||
}
|
||||
|
||||
protected ModificationStatement prepareInternal(CFMetaData cfm, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException
|
||||
@Override
|
||||
protected ModificationStatement prepareInternal(CFMetaData cfm,
|
||||
VariableSpecifications boundNames,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
{
|
||||
UpdateStatement stmt = new UpdateStatement(ModificationStatement.StatementType.UPDATE, boundNames.size(), cfm, attrs);
|
||||
Operations operations = new Operations();
|
||||
|
||||
for (Pair<ColumnIdentifier.Raw, Operation.RawUpdate> entry : updates)
|
||||
{
|
||||
ColumnDefinition def = cfm.getColumnDefinition(entry.left.prepare(cfm));
|
||||
if (def == null)
|
||||
throw new InvalidRequestException(String.format("Unknown identifier %s", entry.left));
|
||||
ColumnDefinition def = getColumnDefinition(cfm, entry.left);
|
||||
|
||||
checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY part %s found in SET part", def.name);
|
||||
|
||||
Operation operation = entry.right.prepare(keyspace(), def);
|
||||
operation.collectMarkerSpecification(boundNames);
|
||||
|
||||
switch (def.kind)
|
||||
{
|
||||
case PARTITION_KEY:
|
||||
case CLUSTERING:
|
||||
throw new InvalidRequestException(String.format("PRIMARY KEY part %s found in SET part", entry.left));
|
||||
default:
|
||||
stmt.addOperation(operation);
|
||||
break;
|
||||
}
|
||||
operations.add(operation);
|
||||
}
|
||||
|
||||
stmt.processWhereClause(whereClause, boundNames);
|
||||
return stmt;
|
||||
StatementRestrictions restrictions = newRestrictions(StatementType.UPDATE,
|
||||
cfm,
|
||||
boundNames,
|
||||
operations,
|
||||
whereClause,
|
||||
conditions);
|
||||
|
||||
return new UpdateStatement(StatementType.UPDATE,
|
||||
boundNames.size(),
|
||||
cfm,
|
||||
operations,
|
||||
restrictions,
|
||||
conditions,
|
||||
attrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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.statements;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.cassandra.db.CounterMutation;
|
||||
|
||||
import org.apache.cassandra.db.Mutation;
|
||||
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
||||
import org.apache.cassandra.db.PartitionColumns;
|
||||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.db.*;
|
||||
|
||||
/**
|
||||
* Utility class to collect updates.
|
||||
*
|
||||
* <p>In a batch statement we don't want to recreate mutations every time as this is particularly inefficient when
|
||||
* applying multiple batch to the same partition (see #6737). </p>
|
||||
*
|
||||
*/
|
||||
final class UpdatesCollector
|
||||
{
|
||||
/**
|
||||
* The columns that will be updated.
|
||||
*/
|
||||
private final PartitionColumns updatedColumns;
|
||||
|
||||
/**
|
||||
* The estimated number of updated row.
|
||||
*/
|
||||
private final int updatedRows;
|
||||
|
||||
/**
|
||||
* The mutations per keyspace.
|
||||
*/
|
||||
private final Map<String, Map<ByteBuffer, IMutation>> mutations = new HashMap<>();
|
||||
|
||||
public UpdatesCollector(PartitionColumns updatedColumns, int updatedRows)
|
||||
{
|
||||
super();
|
||||
this.updatedColumns = updatedColumns;
|
||||
this.updatedRows = updatedRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the <code>PartitionUpdate</code> for the specified column family and key. If the update does not
|
||||
* exist it will be created.
|
||||
*
|
||||
* @param cfm the column family meta data
|
||||
* @param dk the partition key
|
||||
* @param consistency the consistency level
|
||||
* @return the <code>PartitionUpdate</code> for the specified column family and key
|
||||
*/
|
||||
public PartitionUpdate getPartitionUpdate(CFMetaData cfm, DecoratedKey dk, ConsistencyLevel consistency)
|
||||
{
|
||||
Mutation mut = getMutation(cfm, dk, consistency);
|
||||
PartitionUpdate upd = mut.get(cfm);
|
||||
if (upd == null)
|
||||
{
|
||||
upd = new PartitionUpdate(cfm, dk, updatedColumns, updatedRows);
|
||||
mut.add(upd);
|
||||
}
|
||||
return upd;
|
||||
}
|
||||
|
||||
private Mutation getMutation(CFMetaData cfm, DecoratedKey dk, ConsistencyLevel consistency)
|
||||
{
|
||||
String ksName = cfm.ksName;
|
||||
IMutation mutation = keyspaceMap(ksName).get(dk.getKey());
|
||||
if (mutation == null)
|
||||
{
|
||||
Mutation mut = new Mutation(ksName, dk);
|
||||
mutation = cfm.isCounter() ? new CounterMutation(mut, consistency) : mut;
|
||||
keyspaceMap(ksName).put(dk.getKey(), mutation);
|
||||
return mut;
|
||||
}
|
||||
return cfm.isCounter() ? ((CounterMutation) mutation).getMutation() : (Mutation) mutation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection containing all the mutations.
|
||||
* @return a collection containing all the mutations.
|
||||
*/
|
||||
public Collection<IMutation> toMutations()
|
||||
{
|
||||
// The case where all statement where on the same keyspace is pretty common
|
||||
if (mutations.size() == 1)
|
||||
return mutations.values().iterator().next().values();
|
||||
|
||||
List<IMutation> ms = new ArrayList<>();
|
||||
for (Map<ByteBuffer, IMutation> ksMap : mutations.values())
|
||||
ms.addAll(ksMap.values());
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key-mutation mappings for the specified keyspace.
|
||||
*
|
||||
* @param ksName the keyspace name
|
||||
* @return the key-mutation mappings for the specified keyspace.
|
||||
*/
|
||||
private Map<ByteBuffer, IMutation> keyspaceMap(String ksName)
|
||||
{
|
||||
Map<ByteBuffer, IMutation> ksMap = mutations.get(ksName);
|
||||
if (ksMap == null)
|
||||
{
|
||||
ksMap = new HashMap<>();
|
||||
mutations.put(ksName, ksMap);
|
||||
}
|
||||
return ksMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@
|
|||
package org.apache.cassandra.db;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -193,17 +192,17 @@ public abstract class CBuilder
|
|||
|
||||
public Clustering buildWith(ByteBuffer value)
|
||||
{
|
||||
assert size+1 == type.size();
|
||||
assert size+1 <= type.size();
|
||||
|
||||
ByteBuffer[] newValues = Arrays.copyOf(values, size+1);
|
||||
ByteBuffer[] newValues = Arrays.copyOf(values, type.size());
|
||||
newValues[size] = value;
|
||||
return new Clustering(newValues);
|
||||
}
|
||||
|
||||
public Clustering buildWith(List<ByteBuffer> newValues)
|
||||
{
|
||||
assert size + newValues.size() == type.size();
|
||||
ByteBuffer[] buffers = Arrays.copyOf(values, size + newValues.size());
|
||||
assert size + newValues.size() <= type.size();
|
||||
ByteBuffer[] buffers = Arrays.copyOf(values, type.size());
|
||||
int newSize = size;
|
||||
for (ByteBuffer value : newValues)
|
||||
buffers[newSize++] = value;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ package org.apache.cassandra.db;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.cassandra.db.marshal.AbstractType;
|
||||
import org.apache.cassandra.db.rows.RangeTombstoneMarker;
|
||||
|
|
@ -27,6 +28,7 @@ import org.apache.cassandra.io.util.DataInputPlus;
|
|||
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||
import org.apache.cassandra.utils.memory.AbstractAllocator;
|
||||
|
||||
|
||||
/**
|
||||
* A range tombstone is a tombstone that covers a slice/range of rows.
|
||||
* <p>
|
||||
|
|
|
|||
|
|
@ -153,6 +153,15 @@ public abstract class Slices implements Iterable<Slice>
|
|||
|
||||
public abstract String toCQLString(CFMetaData metadata);
|
||||
|
||||
/**
|
||||
* Checks if this <code>Slices</code> is empty.
|
||||
* @return <code>true</code> if this <code>Slices</code> is empty, <code>false</code> otherwise.
|
||||
*/
|
||||
public final boolean isEmpty()
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* In simple object that allows to test the inclusion of rows in those slices assuming those rows
|
||||
* are passed (to {@link #includes}) in clustering order (or reverse clustering ordered, depending
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ public class CQLSSTableWriter implements Closeable
|
|||
|
||||
QueryOptions options = QueryOptions.forInternalCalls(null, values);
|
||||
List<ByteBuffer> keys = insert.buildPartitionKeyNames(options);
|
||||
CBuilder clustering = insert.createClustering(options);
|
||||
SortedSet<Clustering> clusterings = insert.createClustering(options);
|
||||
|
||||
long now = System.currentTimeMillis() * 1000;
|
||||
// Note that we asks indexes to not validate values (the last 'false' arg below) because that triggers a 'Keyspace.open'
|
||||
|
|
@ -222,7 +222,10 @@ public class CQLSSTableWriter implements Closeable
|
|||
try
|
||||
{
|
||||
for (ByteBuffer key : keys)
|
||||
insert.addUpdateForKey(writer.getUpdateFor(key), clustering, params);
|
||||
{
|
||||
for (Clustering clustering : clusterings)
|
||||
insert.addUpdateForKey(writer.getUpdateFor(key), clustering, params);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
catch (SSTableSimpleUnsortedWriter.SyncException e)
|
||||
|
|
|
|||
|
|
@ -447,6 +447,39 @@ public class MaterializedViewTest extends CQLTester
|
|||
Assert.assertEquals(1, execute("select * from mv").size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeTombstone3() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (" +
|
||||
"k int, " +
|
||||
"asciival ascii, " +
|
||||
"bigintval bigint, " +
|
||||
"textval1 text, " +
|
||||
"PRIMARY KEY((k, asciival), bigintval)" +
|
||||
")");
|
||||
|
||||
execute("USE " + keyspace());
|
||||
executeNet(protocolVersion, "USE " + keyspace());
|
||||
|
||||
createView("mv", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s WHERE textval1 IS NOT NULL AND k IS NOT NULL AND asciival IS NOT NULL AND bigintval IS NOT NULL PRIMARY KEY ((textval1, k), asciival, bigintval)");
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
updateMV("INSERT into %s (k,asciival,bigintval,textval1)VALUES(?,?,?,?)", 0, "foo", (long) i % 2, "bar" + i);
|
||||
|
||||
Assert.assertEquals(1, execute("select * from %s where k = 0 and asciival = 'foo' and bigintval = 0").size());
|
||||
Assert.assertEquals(1, execute("select * from %s where k = 0 and asciival = 'foo' and bigintval = 1").size());
|
||||
|
||||
|
||||
Assert.assertEquals(2, execute("select * from %s").size());
|
||||
Assert.assertEquals(2, execute("select * from mv").size());
|
||||
|
||||
//Write a RT and verify the data is removed from index
|
||||
updateMV("DELETE FROM %s WHERE k = ? AND asciival = ? and bigintval >= ?", 0, "foo", 0L);
|
||||
|
||||
Assert.assertEquals(0, execute("select * from %s").size());
|
||||
Assert.assertEquals(0, execute("select * from mv").size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompoundPartitionKey() throws Throwable
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ import com.google.common.collect.ImmutableSet;
|
|||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.cassandra.auth.*;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
|
|
@ -49,8 +47,6 @@ import static org.junit.Assert.fail;
|
|||
|
||||
public class UFAuthTest extends CQLTester
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(UFAuthTest.class);
|
||||
|
||||
String roleName = "test_role";
|
||||
AuthenticatedUser user;
|
||||
RoleResource role;
|
||||
|
|
@ -319,14 +315,14 @@ public class UFAuthTest extends CQLTester
|
|||
public void systemFunctionsRequireNoExplicitPrivileges() throws Throwable
|
||||
{
|
||||
// with terminal arguments, so evaluated at prepare time
|
||||
String cql = String.format("UPDATE %s SET v2 = 0 WHERE k = blobasint(intasblob(0))",
|
||||
String cql = String.format("UPDATE %s SET v2 = 0 WHERE k = blobasint(intasblob(0)) and v1 = 0",
|
||||
KEYSPACE + "." + currentTable());
|
||||
getStatement(cql).checkAccess(clientState);
|
||||
|
||||
// with non-terminal arguments, so evaluated at execution
|
||||
String functionName = createSimpleFunction();
|
||||
grantExecuteOnFunction(functionName);
|
||||
cql = String.format("UPDATE %s SET v2 = 0 WHERE k = blobasint(intasblob(%s))",
|
||||
cql = String.format("UPDATE %s SET v2 = 0 WHERE k = blobasint(intasblob(%s)) and v1 = 0",
|
||||
KEYSPACE + "." + currentTable(),
|
||||
functionCall(functionName));
|
||||
getStatement(cql).checkAccess(clientState);
|
||||
|
|
|
|||
|
|
@ -97,15 +97,15 @@ public class UFIdentificationTest extends CQLTester
|
|||
@Test
|
||||
public void testSimpleModificationStatement() throws Throwable
|
||||
{
|
||||
assertFunctions(cql("INSERT INTO %s (key, t_sc) VALUES (0, %s)", functionCall(tFunc, "'foo'")), tFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc) VALUES (0, %s)", functionCall(iFunc, "1")), iFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, t_cc) VALUES (0, %s)", functionCall(tFunc, "'foo'")), tFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_val) VALUES (0, %s)", functionCall(iFunc, "1")), iFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, l_val) VALUES (0, %s)", functionCall(lFunc, "[1]")), lFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, s_val) VALUES (0, %s)", functionCall(sFunc, "{1}")), sFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, m_val) VALUES (0, %s)", functionCall(mFunc, "{1:1}")), mFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, udt_val) VALUES (0,%s)", functionCall(udtFunc, "{i : 1, t : 'foo'}")), udtFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, u_val) VALUES (0, %s)", functionCall(uFunc, "now()")), uFunc, "system.now");
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, t_sc) VALUES (0, 0, 'A', %s)", functionCall(tFunc, "'foo'")), tFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc) VALUES (0, %s, 'A')", functionCall(iFunc, "1")), iFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, t_cc, i_cc) VALUES (0, %s, 1)", functionCall(tFunc, "'foo'")), tFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, i_val) VALUES (0, 0, 'A', %s)", functionCall(iFunc, "1")), iFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, l_val) VALUES (0, 0, 'A', %s)", functionCall(lFunc, "[1]")), lFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, s_val) VALUES (0, 0, 'A', %s)", functionCall(sFunc, "{1}")), sFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, m_val) VALUES (0, 0, 'A', %s)", functionCall(mFunc, "{1:1}")), mFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, udt_val) VALUES (0, 0, 'A', %s)", functionCall(udtFunc, "{i : 1, t : 'foo'}")), udtFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, u_val) VALUES (0, 0, 'A', %s)", functionCall(uFunc, "now()")), uFunc, "system.now");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -113,48 +113,48 @@ public class UFIdentificationTest extends CQLTester
|
|||
{
|
||||
String iFunc2 = createEchoFunction("int");
|
||||
String mapValue = String.format("{%s:%s}", functionCall(iFunc, "1"), functionCall(iFunc2, "1"));
|
||||
assertFunctions(cql("INSERT INTO %s (key, m_val) VALUES (0, %s)", mapValue), iFunc, iFunc2);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, m_val) VALUES (0, 0, 'A', %s)", mapValue), iFunc, iFunc2);
|
||||
|
||||
String listValue = String.format("[%s]", functionCall(iFunc, "1"));
|
||||
assertFunctions(cql("INSERT INTO %s (key, l_val) VALUES (0, %s)", listValue), iFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, l_val) VALUES (0, 0, 'A', %s)", listValue), iFunc);
|
||||
|
||||
String setValue = String.format("{%s}", functionCall(iFunc, "1"));
|
||||
assertFunctions(cql("INSERT INTO %s (key, s_val) VALUES (0, %s)", setValue), iFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, s_val) VALUES (0, 0, 'A', %s)", setValue), iFunc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonTerminalUDTLiterals() throws Throwable
|
||||
{
|
||||
String udtValue = String.format("{ i: %s, t : %s } ", functionCall(iFunc, "1"), functionCall(tFunc, "'foo'"));
|
||||
assertFunctions(cql("INSERT INTO %s (key, udt_val) VALUES (0, %s)", udtValue), iFunc, tFunc);
|
||||
assertFunctions(cql("INSERT INTO %s (key, i_cc, t_cc, udt_val) VALUES (0, 0, 'A', %s)", udtValue), iFunc, tFunc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModificationStatementWithConditions() throws Throwable
|
||||
{
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF t_sc=%s", functionCall(tFunc, "'foo'")), tFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF i_val=%s", functionCall(iFunc, "1")), iFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF l_val=%s", functionCall(lFunc, "[1]")), lFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF s_val=%s", functionCall(sFunc, "{1}")), sFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF m_val=%s", functionCall(mFunc, "{1:1}")), mFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF t_sc=%s", functionCall(tFunc, "'foo'")), tFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF i_val=%s", functionCall(iFunc, "1")), iFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF l_val=%s", functionCall(lFunc, "[1]")), lFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF s_val=%s", functionCall(sFunc, "{1}")), sFunc);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF m_val=%s", functionCall(mFunc, "{1:1}")), mFunc);
|
||||
|
||||
|
||||
String iFunc2 = createEchoFunction("int");
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF i_val IN (%s, %S)",
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF i_val IN (%s, %S)",
|
||||
functionCall(iFunc, "1"),
|
||||
functionCall(iFunc2, "2")),
|
||||
iFunc, iFunc2);
|
||||
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF u_val=%s",
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF u_val=%s",
|
||||
functionCall(uFunc, "now()")),
|
||||
uFunc, "system.now");
|
||||
|
||||
// conditions on collection elements
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF l_val[%s] = %s",
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF l_val[%s] = %s",
|
||||
functionCall(iFunc, "1"),
|
||||
functionCall(iFunc2, "1")),
|
||||
iFunc, iFunc2);
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 IF m_val[%s] = %s",
|
||||
assertFunctions(cql("UPDATE %s SET i_val=0 WHERE key=0 AND i_cc = 0 AND t_cc = 'A' IF m_val[%s] = %s",
|
||||
functionCall(iFunc, "1"),
|
||||
functionCall(iFunc2, "1")),
|
||||
iFunc, iFunc2);
|
||||
|
|
|
|||
|
|
@ -18,8 +18,12 @@
|
|||
|
||||
package org.apache.cassandra.cql3.validation.operations;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isEmpty;
|
||||
|
||||
import org.apache.cassandra.cql3.CQLTester;
|
||||
|
||||
public class BatchTest extends CQLTester
|
||||
|
|
@ -91,16 +95,81 @@ public class BatchTest extends CQLTester
|
|||
createTable("CREATE TABLE %s (k int PRIMARY KEY, s text, i int)");
|
||||
|
||||
// test batch and update
|
||||
String qualifiedTable = keyspace() + "." + currentTable();
|
||||
execute("BEGIN BATCH " +
|
||||
"INSERT INTO %s (k, s, i) VALUES (100, 'batchtext', 7); " +
|
||||
"INSERT INTO " + qualifiedTable + " (k, s, i) VALUES (111, 'batchtext', 7); " +
|
||||
"UPDATE " + qualifiedTable + " SET s=?, i=? WHERE k = 100; " +
|
||||
"UPDATE " + qualifiedTable + " SET s=?, i=? WHERE k=111; " +
|
||||
"INSERT INTO %1$s (k, s, i) VALUES (100, 'batchtext', 7); " +
|
||||
"INSERT INTO %1$s (k, s, i) VALUES (111, 'batchtext', 7); " +
|
||||
"UPDATE %1$s SET s=?, i=? WHERE k = 100; " +
|
||||
"UPDATE %1$s SET s=?, i=? WHERE k=111; " +
|
||||
"APPLY BATCH;", null, unset(), unset(), null);
|
||||
assertRows(execute("SELECT k, s, i FROM %s where k in (100,111)"),
|
||||
row(100, null, 7),
|
||||
row(111, "batchtext", null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchRangeDelete() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering)) WITH COMPACT STORAGE");
|
||||
|
||||
int value = 0;
|
||||
for (int partitionKey = 0; partitionKey < 4; partitionKey++)
|
||||
for (int clustering1 = 0; clustering1 < 5; clustering1++)
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (?, ?, ?)",
|
||||
partitionKey, clustering1, value++);
|
||||
|
||||
execute("BEGIN BATCH " +
|
||||
"DELETE FROM %1$s WHERE partitionKey = 1;" +
|
||||
"DELETE FROM %1$s WHERE partitionKey = 0 AND clustering >= 4;" +
|
||||
"DELETE FROM %1$s WHERE partitionKey = 0 AND clustering <= 0;" +
|
||||
"DELETE FROM %1$s WHERE partitionKey = 2 AND clustering >= 0 AND clustering <= 3;" +
|
||||
"DELETE FROM %1$s WHERE partitionKey = 2 AND clustering <= 3 AND clustering >= 4;" +
|
||||
"DELETE FROM %1$s WHERE partitionKey = 3 AND (clustering) >= (3) AND (clustering) <= (6);" +
|
||||
"APPLY BATCH;");
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(0, 1, 1),
|
||||
row(0, 2, 2),
|
||||
row(0, 3, 3),
|
||||
row(2, 4, 14),
|
||||
row(3, 0, 15),
|
||||
row(3, 1, 16),
|
||||
row(3, 2, 17));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchUpdate() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1))");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 2, 2)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 3, 3)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 4, 4)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 5, 5)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 6, 6)");
|
||||
|
||||
execute("BEGIN BATCH " +
|
||||
"UPDATE %1$s SET value = 7 WHERE partitionKey = 0 AND clustering_1 = 1" +
|
||||
"UPDATE %1$s SET value = 8 WHERE partitionKey = 0 AND (clustering_1) = (2)" +
|
||||
"UPDATE %1$s SET value = 10 WHERE partitionKey = 0 AND clustering_1 IN (3, 4)" +
|
||||
"UPDATE %1$s SET value = 20 WHERE partitionKey = 0 AND (clustering_1) IN ((5), (6))" +
|
||||
"APPLY BATCH;");
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(0, 0, 0),
|
||||
row(0, 1, 7),
|
||||
row(0, 2, 8),
|
||||
row(0, 3, 10),
|
||||
row(0, 4, 10),
|
||||
row(0, 5, 20),
|
||||
row(0, 6, 20));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import java.util.List;
|
|||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.cql3.CQLTester;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isEmpty;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class DeleteTest extends CQLTester
|
||||
|
|
@ -326,4 +326,683 @@ public class DeleteTest extends CQLTester
|
|||
|
||||
assertEmpty(execute("select * from %s where a=1 and b=1"));
|
||||
}
|
||||
@Test
|
||||
public void testDeleteWithNoClusteringColumns() throws Throwable
|
||||
{
|
||||
testDeleteWithNoClusteringColumns(false);
|
||||
testDeleteWithNoClusteringColumns(true);
|
||||
}
|
||||
|
||||
private void testDeleteWithNoClusteringColumns(boolean forceFlush) throws Throwable
|
||||
{
|
||||
for (String compactOption : new String[] {"", " WITH COMPACT STORAGE" })
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int PRIMARY KEY," +
|
||||
"value int)" + compactOption);
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, value) VALUES (0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, value) VALUES (1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey, value) VALUES (2, 2)");
|
||||
execute("INSERT INTO %s (partitionKey, value) VALUES (3, 3)");
|
||||
flush(forceFlush);
|
||||
|
||||
execute("DELETE value FROM %s WHERE partitionKey = ?", 0);
|
||||
flush(forceFlush);
|
||||
|
||||
if (isEmpty(compactOption))
|
||||
{
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
|
||||
row(0, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 0));
|
||||
}
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey IN (?, ?)", 0, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(2, 2),
|
||||
row(3, 3));
|
||||
|
||||
// test invalid queries
|
||||
|
||||
// token function
|
||||
assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
|
||||
"DELETE FROM %s WHERE token(partitionKey) = token(?)", 0);
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("partitionkey cannot be restricted by more than one relation if it includes an Equal",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND partitionKey = ?", 0, 1);
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier unknown",
|
||||
"DELETE unknown FROM %s WHERE partitionKey = ?", 0);
|
||||
|
||||
assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
|
||||
"DELETE FROM %s WHERE partitionKey1 = ?", 0);
|
||||
|
||||
// Invalid operator in the where clause
|
||||
assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
|
||||
"DELETE FROM %s WHERE partitionKey > ? ", 0);
|
||||
|
||||
assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
|
||||
"DELETE FROM %s WHERE partitionKey CONTAINS ?", 0);
|
||||
|
||||
// Non primary key in the where clause
|
||||
assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND value = ?", 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithOneClusteringColumns() throws Throwable
|
||||
{
|
||||
testDeleteWithOneClusteringColumns(false);
|
||||
testDeleteWithOneClusteringColumns(true);
|
||||
}
|
||||
|
||||
private void testDeleteWithOneClusteringColumns(boolean forceFlush) throws Throwable
|
||||
{
|
||||
for (String compactOption : new String[] {"", " WITH COMPACT STORAGE" })
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering))" + compactOption);
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 2, 2)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 3, 3)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 4, 4)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 5, 5)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (1, 0, 6)");
|
||||
flush(forceFlush);
|
||||
|
||||
execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1);
|
||||
flush(forceFlush);
|
||||
if (isEmpty(compactOption))
|
||||
{
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1),
|
||||
row(0, 1, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1));
|
||||
}
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) = (?)", 0, 1);
|
||||
flush(forceFlush);
|
||||
assertEmpty(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey IN (?, ?) AND clustering = ?", 0, 1, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
|
||||
row(0, 2, 2),
|
||||
row(0, 3, 3),
|
||||
row(0, 4, 4),
|
||||
row(0, 5, 5));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) IN ((?), (?))", 0, 4, 5);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
|
||||
row(0, 2, 2),
|
||||
row(0, 3, 3));
|
||||
|
||||
// test invalid queries
|
||||
|
||||
// missing primary key element
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"DELETE FROM %s WHERE clustering = ?", 1);
|
||||
|
||||
// token function
|
||||
assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
|
||||
"DELETE FROM %s WHERE token(partitionKey) = token(?) AND clustering = ? ", 0, 1);
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("clustering cannot be restricted by more than one relation if it includes an Equal",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering = ? AND clustering = ?", 0, 1, 1);
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier value1",
|
||||
"DELETE value1 FROM %s WHERE partitionKey = ? AND clustering = ?", 0, 1);
|
||||
|
||||
assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
|
||||
"DELETE FROM %s WHERE partitionKey1 = ? AND clustering = ?", 0, 1);
|
||||
|
||||
assertInvalidMessage("Undefined name clustering_3 in where clause ('clustering_3 = ?')",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering_3 = ?", 0, 1);
|
||||
|
||||
// Invalid operator in the where clause
|
||||
assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
|
||||
"DELETE FROM %s WHERE partitionKey > ? AND clustering = ?", 0, 1);
|
||||
|
||||
assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
|
||||
"DELETE FROM %s WHERE partitionKey CONTAINS ? AND clustering = ?", 0, 1);
|
||||
|
||||
// Non primary key in the where clause
|
||||
String errorMsg = isEmpty(compactOption) ? "Non PRIMARY KEY columns found in where clause: value"
|
||||
: "Predicates on the non-primary-key column (value) of a COMPACT table are not yet supported";
|
||||
|
||||
assertInvalidMessage(errorMsg,
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering = ? AND value = ?", 0, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithTwoClusteringColumns() throws Throwable
|
||||
{
|
||||
testDeleteWithTwoClusteringColumns(false);
|
||||
testDeleteWithTwoClusteringColumns(true);
|
||||
}
|
||||
|
||||
private void testDeleteWithTwoClusteringColumns(boolean forceFlush) throws Throwable
|
||||
{
|
||||
for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1, clustering_2))" + compactOption);
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 2, 2)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 3, 3)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 1, 1, 4)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 1, 2, 5)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (1, 0, 0, 6)");
|
||||
flush(forceFlush);
|
||||
|
||||
execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
|
||||
flush(forceFlush);
|
||||
|
||||
if (isEmpty(compactOption))
|
||||
{
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 1, 1),
|
||||
row(0, 1, 1, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 1, 1));
|
||||
}
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) = (?, ?)", 0, 1, 1);
|
||||
flush(forceFlush);
|
||||
assertEmpty(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 1, 1));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey IN (?, ?) AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 0, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
|
||||
row(0, 0, 1, 1),
|
||||
row(0, 0, 2, 2),
|
||||
row(0, 0, 3, 3),
|
||||
row(0, 1, 2, 5));
|
||||
|
||||
Object[][] rows;
|
||||
if (isEmpty(compactOption))
|
||||
{
|
||||
rows = new Object[][]{row(0, 0, 1, 1),
|
||||
row(0, 0, 2, null),
|
||||
row(0, 0, 3, null),
|
||||
row(0, 1, 2, 5)};
|
||||
}
|
||||
else
|
||||
{
|
||||
rows = new Object[][]{row(0, 0, 1, 1), row(0, 1, 2, 5)};
|
||||
}
|
||||
|
||||
execute("DELETE value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 IN (?, ?)", 0, 0, 2, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1), rows);
|
||||
|
||||
if (isEmpty(compactOption))
|
||||
{
|
||||
rows = new Object[][]{row(0, 0, 1, 1),
|
||||
row(0, 0, 3, null)};
|
||||
}
|
||||
else
|
||||
{
|
||||
rows = new Object[][]{row(0, 0, 1, 1)};
|
||||
}
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) IN ((?, ?), (?, ?))", 0, 0, 2, 1, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1), rows);
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?)) AND clustering_2 = ?", 0, 0, 2, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
|
||||
row(0, 0, 1, 1));
|
||||
|
||||
// test invalid queries
|
||||
|
||||
// missing primary key element
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"DELETE FROM %s WHERE clustering_1 = ? AND clustering_2 = ?", 1, 1);
|
||||
|
||||
assertInvalidMessage("PRIMARY KEY column \"clustering_2\" cannot be restricted as preceding column \"clustering_1\" is not restricted",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering_2 = ?", 0, 1);
|
||||
|
||||
// token function
|
||||
assertInvalidMessage("The token function cannot be used in WHERE clauses for DELETE statements",
|
||||
"DELETE FROM %s WHERE token(partitionKey) = token(?) AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("clustering_1 cannot be restricted by more than one relation if it includes an Equal",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ? AND clustering_1 = ?", 0, 1, 1, 1);
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier value1",
|
||||
"DELETE value1 FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
|
||||
|
||||
assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
|
||||
"DELETE FROM %s WHERE partitionKey1 = ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
|
||||
|
||||
assertInvalidMessage("Undefined name clustering_3 in where clause ('clustering_3 = ?')",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_3 = ?", 0, 1, 1);
|
||||
|
||||
// Invalid operator in the where clause
|
||||
assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
|
||||
"DELETE FROM %s WHERE partitionKey > ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
|
||||
|
||||
assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
|
||||
"DELETE FROM %s WHERE partitionKey CONTAINS ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1);
|
||||
|
||||
// Non primary key in the where clause
|
||||
String errorMsg = isEmpty(compactOption) ? "Non PRIMARY KEY columns found in where clause: value"
|
||||
: "Predicates on the non-primary-key column (value) of a COMPACT table are not yet supported";
|
||||
|
||||
assertInvalidMessage(errorMsg,
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ? AND value = ?", 0, 1, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithRangeAndOneClusteringColumn() throws Throwable
|
||||
{
|
||||
testDeleteWithRangeAndOneClusteringColumn(false);
|
||||
testDeleteWithRangeAndOneClusteringColumn(true);
|
||||
}
|
||||
|
||||
private void testDeleteWithRangeAndOneClusteringColumn(boolean forceFlush) throws Throwable
|
||||
{
|
||||
for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering))" + compactOption);
|
||||
|
||||
int value = 0;
|
||||
for (int partitionKey = 0; partitionKey < 5; partitionKey++)
|
||||
for (int clustering1 = 0; clustering1 < 5; clustering1++)
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (?, ?, ?)",
|
||||
partitionKey, clustering1, value++);
|
||||
|
||||
flush(forceFlush);
|
||||
|
||||
// test delete partition
|
||||
execute("DELETE FROM %s WHERE partitionKey = ?", 1);
|
||||
flush(forceFlush);
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 1));
|
||||
|
||||
// test slices on the first clustering column
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering >= ?", 0, 4);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
|
||||
row(0, 0, 0),
|
||||
row(0, 1, 1),
|
||||
row(0, 2, 2),
|
||||
row(0, 3, 3));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering > ?", 0, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
|
||||
row(0, 0, 0),
|
||||
row(0, 1, 1),
|
||||
row(0, 2, 2));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering <= ?", 0, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
|
||||
row(0, 1, 1),
|
||||
row(0, 2, 2));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering < ?", 0, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
|
||||
row(0, 2, 2));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering >= ? AND clustering < ?", 2, 0, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 13),
|
||||
row(2, 4, 14));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering > ? AND clustering <= ?", 2, 3, 5);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 13));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering < ? AND clustering > ?", 2, 3, 5);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 13));
|
||||
|
||||
// test multi-column slices
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) > (?)", 3, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
|
||||
row(3, 0, 15),
|
||||
row(3, 1, 16),
|
||||
row(3, 2, 17));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) < (?)", 3, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
|
||||
row(3, 1, 16),
|
||||
row(3, 2, 17));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) >= (?) AND (clustering) <= (?)", 3, 0, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
|
||||
row(3, 2, 17));
|
||||
|
||||
// Test invalid queries
|
||||
assertInvalidMessage("Range deletions are not supported for specific columns",
|
||||
"DELETE value FROM %s WHERE partitionKey = ? AND clustering >= ?", 2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithRangeAndTwoClusteringColumns() throws Throwable
|
||||
{
|
||||
testDeleteWithRangeAndTwoClusteringColumns(false);
|
||||
testDeleteWithRangeAndTwoClusteringColumns(true);
|
||||
}
|
||||
|
||||
private void testDeleteWithRangeAndTwoClusteringColumns(boolean forceFlush) throws Throwable
|
||||
{
|
||||
for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1, clustering_2))" + compactOption);
|
||||
|
||||
int value = 0;
|
||||
for (int partitionKey = 0; partitionKey < 5; partitionKey++)
|
||||
for (int clustering1 = 0; clustering1 < 5; clustering1++)
|
||||
for (int clustering2 = 0; clustering2 < 5; clustering2++) {
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (?, ?, ?, ?)",
|
||||
partitionKey, clustering1, clustering2, value++);}
|
||||
flush(forceFlush);
|
||||
|
||||
// test unspecified second clustering column
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
|
||||
row(0, 0, 0, 0),
|
||||
row(0, 0, 1, 1),
|
||||
row(0, 0, 2, 2),
|
||||
row(0, 0, 3, 3),
|
||||
row(0, 0, 4, 4));
|
||||
|
||||
// test delete partition
|
||||
execute("DELETE FROM %s WHERE partitionKey = ?", 1);
|
||||
flush(forceFlush);
|
||||
assertEmpty(execute("SELECT * FROM %s WHERE partitionKey = ?", 1));
|
||||
|
||||
// test slices on the second clustering column
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 < ?", 0, 0, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
|
||||
row(0, 0, 2, 2),
|
||||
row(0, 0, 3, 3),
|
||||
row(0, 0, 4, 4));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 <= ?", 0, 0, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 2),
|
||||
row(0, 0, 4, 4));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? ", 0, 2, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 2),
|
||||
row(0, 2, 0, 10),
|
||||
row(0, 2, 1, 11),
|
||||
row(0, 2, 2, 12));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 >= ? ", 0, 2, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 2),
|
||||
row(0, 2, 0, 10));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? AND clustering_2 < ? ",
|
||||
0, 3, 1, 4);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
|
||||
row(0, 3, 0, 15),
|
||||
row(0, 3, 1, 16),
|
||||
row(0, 3, 4, 19));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 > ? AND clustering_2 < ? ",
|
||||
0, 3, 4, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
|
||||
row(0, 3, 0, 15),
|
||||
row(0, 3, 1, 16),
|
||||
row(0, 3, 4, 19));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 >= ? AND clustering_2 <= ? ",
|
||||
0, 3, 1, 4);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 0, 3),
|
||||
row(0, 3, 0, 15));
|
||||
|
||||
// test slices on the first clustering column
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 >= ?", 0, 4);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
|
||||
row(0, 0, 4, 4),
|
||||
row(0, 2, 0, 10),
|
||||
row(0, 3, 0, 15));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 > ?", 0, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
|
||||
row(0, 0, 4, 4),
|
||||
row(0, 2, 0, 10),
|
||||
row(0, 3, 0, 15));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 < ?", 0, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 0),
|
||||
row(0, 3, 0, 15));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 >= ? AND clustering_1 < ?", 2, 0, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 0, 65),
|
||||
row(2, 3, 1, 66),
|
||||
row(2, 3, 2, 67),
|
||||
row(2, 3, 3, 68),
|
||||
row(2, 3, 4, 69),
|
||||
row(2, 4, 0, 70),
|
||||
row(2, 4, 1, 71),
|
||||
row(2, 4, 2, 72),
|
||||
row(2, 4, 3, 73),
|
||||
row(2, 4, 4, 74));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 > ? AND clustering_1 <= ?", 2, 3, 5);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 0, 65),
|
||||
row(2, 3, 1, 66),
|
||||
row(2, 3, 2, 67),
|
||||
row(2, 3, 3, 68),
|
||||
row(2, 3, 4, 69));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 < ? AND clustering_1 > ?", 2, 3, 5);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 0, 65),
|
||||
row(2, 3, 1, 66),
|
||||
row(2, 3, 2, 67),
|
||||
row(2, 3, 3, 68),
|
||||
row(2, 3, 4, 69));
|
||||
|
||||
// test multi-column slices
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) > (?, ?)", 2, 3, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 0, 65),
|
||||
row(2, 3, 1, 66),
|
||||
row(2, 3, 2, 67),
|
||||
row(2, 3, 3, 68));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) < (?, ?)", 2, 3, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 1, 66),
|
||||
row(2, 3, 2, 67),
|
||||
row(2, 3, 3, 68));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?) AND (clustering_1) <= (?)", 2, 3, 2, 4);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
|
||||
row(2, 3, 1, 66));
|
||||
|
||||
// Test with a mix of single column and multi-column restrictions
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND (clustering_2) < (?)", 3, 0, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ?", 3, 0),
|
||||
row(3, 0, 3, 78),
|
||||
row(3, 0, 4, 79));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?) AND (clustering_2) >= (?)", 3, 0, 1, 3);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
|
||||
row(3, 1, 0, 80),
|
||||
row(3, 1, 1, 81),
|
||||
row(3, 1, 2, 82));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?)) AND clustering_2 < ?", 3, 0, 1, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
|
||||
row(3, 1, 1, 81),
|
||||
row(3, 1, 2, 82));
|
||||
|
||||
execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1) = (?) AND clustering_2 >= ?", 3, 1, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 3, 0, 1),
|
||||
row(3, 1, 1, 81));
|
||||
|
||||
// Test invalid queries
|
||||
assertInvalidMessage("Range deletions are not supported for specific columns",
|
||||
"DELETE value FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?)", 2, 3, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithAStaticColumn() throws Throwable
|
||||
{
|
||||
testDeleteWithAStaticColumn(false);
|
||||
testDeleteWithAStaticColumn(true);
|
||||
}
|
||||
|
||||
private void testDeleteWithAStaticColumn(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
"staticValue text static," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1, clustering_2))");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value, staticValue) VALUES (0, 0, 0, 0, 'A')");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value, staticValue) VALUES (1, 0, 0, 6, 'B')");
|
||||
flush(forceFlush);
|
||||
|
||||
execute("DELETE staticValue FROM %s WHERE partitionKey = ?", 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT DISTINCT staticValue FROM %s WHERE partitionKey IN (?, ?)", 0, 1),
|
||||
row(new Object[1]), row("B"));
|
||||
|
||||
execute("DELETE staticValue, value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
1, 0, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, 0, 0, null, null),
|
||||
row(0, 0, 0, null, 0),
|
||||
row(0, 0, 1, null, 1));
|
||||
|
||||
assertInvalidMessage("Invalid restrictions on clustering columns since the DELETE statement modifies only static columns",
|
||||
"DELETE staticValue FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 0, 1);
|
||||
|
||||
assertInvalidMessage("Invalid restrictions on clustering columns since the DELETE statement modifies only static columns",
|
||||
"DELETE staticValue FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) >= (?, ?)",
|
||||
0, 0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithSecondaryIndices() throws Throwable
|
||||
{
|
||||
testDeleteWithSecondaryIndices(false);
|
||||
testDeleteWithSecondaryIndices(true);
|
||||
}
|
||||
|
||||
private void testDeleteWithSecondaryIndices(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"value int," +
|
||||
"values set<int>," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1))");
|
||||
|
||||
createIndex("CREATE INDEX ON %s (value)");
|
||||
createIndex("CREATE INDEX ON %s (clustering_1)");
|
||||
createIndex("CREATE INDEX ON %s (values)");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 0, 0, {0})");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 1, 1, {0, 1})");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 2, 2, {0, 1, 2})");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 3, 3, {0, 1, 2, 3})");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (1, 0, 4, {0, 1, 2, 3, 4})");
|
||||
|
||||
flush(forceFlush);
|
||||
|
||||
assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND value = ?", 3, 3, 3);
|
||||
assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND values CONTAINS ?", 3, 3, 3);
|
||||
assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND value = ?", 3, 3);
|
||||
assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values",
|
||||
"DELETE FROM %s WHERE partitionKey = ? AND values CONTAINS ?", 3, 3);
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"DELETE FROM %s WHERE clustering_1 = ?", 3);
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"DELETE FROM %s WHERE value = ?", 3);
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"DELETE FROM %s WHERE values CONTAINS ?", 3);
|
||||
}
|
||||
|
||||
private void flush(boolean forceFlush)
|
||||
{
|
||||
if (forceFlush)
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,4 +56,237 @@ public class InsertTest extends CQLTester
|
|||
row(new Object[]{ null })
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsert() throws Throwable
|
||||
{
|
||||
testInsert(false);
|
||||
testInsert(true);
|
||||
}
|
||||
|
||||
private void testInsert(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering))");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering) VALUES (0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 1, 1)");
|
||||
flush(forceFlush);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(0, 0, null),
|
||||
row(0, 1, 1));
|
||||
|
||||
// Missing primary key columns
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"INSERT INTO %s (clustering, value) VALUES (0, 1)");
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering",
|
||||
"INSERT INTO %s (partitionKey, value) VALUES (0, 2)");
|
||||
|
||||
// multiple time the same value
|
||||
assertInvalidMessage("The column names contains duplicates",
|
||||
"INSERT INTO %s (partitionKey, clustering, value, value) VALUES (0, 0, 2, 2)");
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("The column names contains duplicates",
|
||||
"INSERT INTO %s (partitionKey, clustering, clustering, value) VALUES (0, 0, 0, 2)");
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier clusteringx",
|
||||
"INSERT INTO %s (partitionKey, clusteringx, value) VALUES (0, 0, 2)");
|
||||
|
||||
assertInvalidMessage("Unknown identifier valuex",
|
||||
"INSERT INTO %s (partitionKey, clustering, valuex) VALUES (0, 0, 2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithCompactFormat() throws Throwable
|
||||
{
|
||||
testInsertWithCompactFormat(false);
|
||||
testInsertWithCompactFormat(true);
|
||||
}
|
||||
|
||||
private void testInsertWithCompactFormat(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering)) WITH COMPACT STORAGE");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering, value) VALUES (0, 1, 1)");
|
||||
flush(forceFlush);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(0, 0, 0),
|
||||
row(0, 1, 1));
|
||||
|
||||
// Invalid Null values for the clustering key or the regular column
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering",
|
||||
"INSERT INTO %s (partitionKey, value) VALUES (0, 0)");
|
||||
assertInvalidMessage("Column value is mandatory for this COMPACT STORAGE table",
|
||||
"INSERT INTO %s (partitionKey, clustering) VALUES (0, 0)");
|
||||
|
||||
// Missing primary key columns
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"INSERT INTO %s (clustering, value) VALUES (0, 1)");
|
||||
|
||||
// multiple time the same value
|
||||
assertInvalidMessage("The column names contains duplicates",
|
||||
"INSERT INTO %s (partitionKey, clustering, value, value) VALUES (0, 0, 2, 2)");
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("The column names contains duplicates",
|
||||
"INSERT INTO %s (partitionKey, clustering, clustering, value) VALUES (0, 0, 0, 2)");
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier clusteringx",
|
||||
"INSERT INTO %s (partitionKey, clusteringx, value) VALUES (0, 0, 2)");
|
||||
|
||||
assertInvalidMessage("Unknown identifier valuex",
|
||||
"INSERT INTO %s (partitionKey, clustering, valuex) VALUES (0, 0, 2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithTwoClusteringColumns() throws Throwable
|
||||
{
|
||||
testInsertWithTwoClusteringColumns(false);
|
||||
testInsertWithTwoClusteringColumns(true);
|
||||
}
|
||||
|
||||
private void testInsertWithTwoClusteringColumns(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1, clustering_2))");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2) VALUES (0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
|
||||
flush(forceFlush);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(0, 0, 0, null),
|
||||
row(0, 0, 1, 1));
|
||||
|
||||
// Missing primary key columns
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"INSERT INTO %s (clustering_1, clustering_2, value) VALUES (0, 0, 1)");
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering_1",
|
||||
"INSERT INTO %s (partitionKey, clustering_2, value) VALUES (0, 0, 2)");
|
||||
|
||||
// multiple time the same value
|
||||
assertInvalidMessage("The column names contains duplicates",
|
||||
"INSERT INTO %s (partitionKey, clustering_1, value, clustering_2, value) VALUES (0, 0, 2, 0, 2)");
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("The column names contains duplicates",
|
||||
"INSERT INTO %s (partitionKey, clustering_1, clustering_1, clustering_2, value) VALUES (0, 0, 0, 0, 2)");
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier clustering_1x",
|
||||
"INSERT INTO %s (partitionKey, clustering_1x, clustering_2, value) VALUES (0, 0, 0, 2)");
|
||||
|
||||
assertInvalidMessage("Unknown identifier valuex",
|
||||
"INSERT INTO %s (partitionKey, clustering_1, clustering_2, valuex) VALUES (0, 0, 0, 2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithCompactStorageAndTwoClusteringColumns() throws Throwable
|
||||
{
|
||||
testInsertWithCompactStorageAndTwoClusteringColumns(false);
|
||||
testInsertWithCompactStorageAndTwoClusteringColumns(true);
|
||||
}
|
||||
|
||||
private void testInsertWithCompactStorageAndTwoClusteringColumns(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1, clustering_2)) WITH COMPACT STORAGE");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
|
||||
flush(forceFlush);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(0, 0, null, 0),
|
||||
row(0, 0, 0, 0),
|
||||
row(0, 0, 1, 1));
|
||||
|
||||
// Invalid Null values for the clustering key or the regular column
|
||||
assertInvalidMessage("PRIMARY KEY column \"clustering_2\" cannot be restricted as preceding column \"clustering_1\" is not restricted",
|
||||
"INSERT INTO %s (partitionKey, clustering_2, value) VALUES (0, 0, 0)");
|
||||
assertInvalidMessage("Column value is mandatory for this COMPACT STORAGE table",
|
||||
"INSERT INTO %s (partitionKey, clustering_1, clustering_2) VALUES (0, 0, 0)");
|
||||
|
||||
// Missing primary key columns
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"INSERT INTO %s (clustering_1, clustering_2, value) VALUES (0, 0, 1)");
|
||||
assertInvalidMessage("PRIMARY KEY column \"clustering_2\" cannot be restricted as preceding column \"clustering_1\" is not restricted",
|
||||
"INSERT INTO %s (partitionKey, clustering_2, value) VALUES (0, 0, 2)");
|
||||
|
||||
// multiple time the same value
|
||||
assertInvalidMessage("The column names contains duplicates",
|
||||
"INSERT INTO %s (partitionKey, clustering_1, value, clustering_2, value) VALUES (0, 0, 2, 0, 2)");
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("The column names contains duplicates",
|
||||
"INSERT INTO %s (partitionKey, clustering_1, clustering_1, clustering_2, value) VALUES (0, 0, 0, 0, 2)");
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier clustering_1x",
|
||||
"INSERT INTO %s (partitionKey, clustering_1x, clustering_2, value) VALUES (0, 0, 0, 2)");
|
||||
|
||||
assertInvalidMessage("Unknown identifier valuex",
|
||||
"INSERT INTO %s (partitionKey, clustering_1, clustering_2, valuex) VALUES (0, 0, 0, 2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertWithAStaticColumn() throws Throwable
|
||||
{
|
||||
testInsertWithAStaticColumn(false);
|
||||
testInsertWithAStaticColumn(true);
|
||||
}
|
||||
|
||||
private void testInsertWithAStaticColumn(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
"staticValue text static," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1, clustering_2))");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, staticValue) VALUES (0, 0, 0, 'A')");
|
||||
execute("INSERT INTO %s (partitionKey, staticValue) VALUES (1, 'B')");
|
||||
flush(forceFlush);
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, null, null, "B", null),
|
||||
row(0, 0, 0, "A", null));
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (1, 0, 0, 0)");
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, 0, 0, "B", 0),
|
||||
row(0, 0, 0, "A", null));
|
||||
|
||||
// Missing primary key columns
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"INSERT INTO %s (clustering_1, clustering_2, staticValue) VALUES (0, 0, 'A')");
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering_1",
|
||||
"INSERT INTO %s (partitionKey, clustering_2, staticValue) VALUES (0, 0, 'A')");
|
||||
}
|
||||
|
||||
private void flush(boolean forceFlush)
|
||||
{
|
||||
if (forceFlush)
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,10 @@ public class InsertUpdateIfConditionTest extends CQLTester
|
|||
|
||||
// Should apply
|
||||
assertRows(execute("DELETE FROM %s WHERE k = 0 IF v1 IN (null)"), row(true));
|
||||
|
||||
createTable(" CREATE TABLE %s (k int, c int, v1 text, PRIMARY KEY(k, c))");
|
||||
assertInvalidMessage("IN on the clustering key columns is not supported with conditional updates",
|
||||
"UPDATE %s SET v1 = 'A' WHERE k = 0 AND c IN (1, 2) IF EXISTS");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -184,11 +188,25 @@ public class InsertUpdateIfConditionTest extends CQLTester
|
|||
assertRows(execute("DELETE FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(false));
|
||||
|
||||
// CASSANDRA-6430
|
||||
assertInvalid("DELETE FROM %s WHERE k = 'k' IF EXISTS");
|
||||
assertInvalid("DELETE FROM %s WHERE k = 'k' IF v = 'foo'");
|
||||
assertInvalid("DELETE FROM %s WHERE i = 0 IF EXISTS");
|
||||
assertInvalid("DELETE FROM %s WHERE k = 0 AND i > 0 IF EXISTS");
|
||||
assertInvalid("DELETE FROM %s WHERE k = 0 AND i > 0 IF v = 'foo'");
|
||||
assertInvalidMessage("DELETE statements must restrict all PRIMARY KEY columns with equality relations in order to use IF conditions",
|
||||
"DELETE FROM %s WHERE k = 'k' IF EXISTS");
|
||||
assertInvalidMessage("DELETE statements must restrict all PRIMARY KEY columns with equality relations in order to use IF conditions",
|
||||
"DELETE FROM %s WHERE k = 'k' IF v = 'foo'");
|
||||
assertInvalidMessage("Some partition key parts are missing: k",
|
||||
"DELETE FROM %s WHERE i = 0 IF EXISTS");
|
||||
|
||||
assertInvalidMessage("Invalid INTEGER constant (0) for \"k\" of type text",
|
||||
"DELETE FROM %s WHERE k = 0 AND i > 0 IF EXISTS");
|
||||
assertInvalidMessage("Invalid INTEGER constant (0) for \"k\" of type text",
|
||||
"DELETE FROM %s WHERE k = 0 AND i > 0 IF v = 'foo'");
|
||||
assertInvalidMessage("DELETE statements must restrict all PRIMARY KEY columns with equality relations in order to use IF conditions",
|
||||
"DELETE FROM %s WHERE k = 'k' AND i > 0 IF EXISTS");
|
||||
assertInvalidMessage("DELETE statements must restrict all PRIMARY KEY columns with equality relations in order to use IF conditions",
|
||||
"DELETE FROM %s WHERE k = 'k' AND i > 0 IF v = 'foo'");
|
||||
assertInvalidMessage("IN on the clustering key columns is not supported with conditional deletions",
|
||||
"DELETE FROM %s WHERE k = 'k' AND i IN (0, 1) IF v = 'foo'");
|
||||
assertInvalidMessage("IN on the clustering key columns is not supported with conditional deletions",
|
||||
"DELETE FROM %s WHERE k = 'k' AND i IN (0, 1) IF EXISTS");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,8 +18,11 @@
|
|||
|
||||
package org.apache.cassandra.cql3.validation.operations;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isEmpty;
|
||||
import org.apache.cassandra.cql3.CQLTester;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
|
||||
|
|
@ -83,4 +86,448 @@ public class UpdateTest extends CQLTester
|
|||
assertInvalid("UPDATE %s SET d = (int)3 WHERE k = ?", 0);
|
||||
assertInvalid("UPDATE %s SET i = (double)3 WHERE k = ?", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws Throwable
|
||||
{
|
||||
testUpdate(false);
|
||||
testUpdate(true);
|
||||
}
|
||||
|
||||
private void testUpdate(boolean forceFlush) throws Throwable
|
||||
{
|
||||
for (String compactOption : new String[] {"", " WITH COMPACT STORAGE" })
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1))" + compactOption);
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 2, 2)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (0, 3, 3)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value) VALUES (1, 0, 4)");
|
||||
|
||||
flush(forceFlush);
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ?", 7, 0, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering_1 = ?",
|
||||
0, 1),
|
||||
row(7));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND (clustering_1) = (?)", 8, 0, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering_1 = ?",
|
||||
0, 2),
|
||||
row(8));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey IN (?, ?) AND clustering_1 = ?", 9, 0, 1, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?) AND clustering_1 = ?",
|
||||
0, 1, 0),
|
||||
row(0, 0, 9),
|
||||
row(1, 0, 9));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey IN ? AND clustering_1 = ?", 19, Arrays.asList(0, 1), 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN ? AND clustering_1 = ?",
|
||||
Arrays.asList(0, 1), 0),
|
||||
row(0, 0, 19),
|
||||
row(1, 0, 19));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 IN (?, ?)", 10, 0, 1, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?)",
|
||||
0, 1, 0),
|
||||
row(0, 0, 10),
|
||||
row(0, 1, 10));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND (clustering_1) IN ((?), (?))", 20, 0, 0, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?))",
|
||||
0, 0, 1),
|
||||
row(0, 0, 20),
|
||||
row(0, 1, 20));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ?", null, 0, 0);
|
||||
flush(forceFlush);
|
||||
|
||||
if (isEmpty(compactOption))
|
||||
{
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?))",
|
||||
0, 0, 1),
|
||||
row(0, 0, null),
|
||||
row(0, 1, 20));
|
||||
}
|
||||
else
|
||||
{
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND (clustering_1) IN ((?), (?))",
|
||||
0, 0, 1),
|
||||
row(0, 1, 20));
|
||||
}
|
||||
|
||||
// test invalid queries
|
||||
|
||||
// missing primary key element
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"UPDATE %s SET value = ? WHERE clustering_1 = ? ", 7, 1);
|
||||
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering_1",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ?", 7, 0);
|
||||
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering_1",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ?", 7, 0);
|
||||
|
||||
// token function
|
||||
assertInvalidMessage("The token function cannot be used in WHERE clauses for UPDATE statements",
|
||||
"UPDATE %s SET value = ? WHERE token(partitionKey) = token(?) AND clustering_1 = ?",
|
||||
7, 0, 1);
|
||||
|
||||
// multiple time the same value
|
||||
assertInvalidSyntax("UPDATE %s SET value = ?, value = ? WHERE partitionKey = ? AND clustering_1 = ?", 7, 0, 1);
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("clustering_1 cannot be restricted by more than one relation if it includes an Equal",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_1 = ?", 7, 0, 1, 1);
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier value1",
|
||||
"UPDATE %s SET value1 = ? WHERE partitionKey = ? AND clustering_1 = ?", 7, 0, 1);
|
||||
|
||||
assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey1 = ? AND clustering_1 = ?", 7, 0, 1);
|
||||
|
||||
assertInvalidMessage("Undefined name clustering_3 in where clause ('clustering_3 = ?')",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_3 = ?", 7, 0, 1);
|
||||
|
||||
// Invalid operator in the where clause
|
||||
assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey > ? AND clustering_1 = ?", 7, 0, 1);
|
||||
|
||||
assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey CONTAINS ? AND clustering_1 = ?", 7, 0, 1);
|
||||
|
||||
String expectedMsg = isEmpty(compactOption) ? "Non PRIMARY KEY columns found in where clause: value"
|
||||
: "Predicates on the non-primary-key column (value) of a COMPACT table are not yet supported";
|
||||
assertInvalidMessage(expectedMsg,
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ? AND value = ?", 7, 0, 1, 3);
|
||||
|
||||
assertInvalidMessage("Slice restrictions are not supported on the clustering columns in UPDATE statements",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 > ?", 7, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithSecondaryIndices() throws Throwable
|
||||
{
|
||||
testUpdateWithSecondaryIndices(false);
|
||||
testUpdateWithSecondaryIndices(true);
|
||||
}
|
||||
|
||||
private void testUpdateWithSecondaryIndices(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"value int," +
|
||||
"values set<int>," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1))");
|
||||
|
||||
createIndex("CREATE INDEX ON %s (value)");
|
||||
createIndex("CREATE INDEX ON %s (clustering_1)");
|
||||
createIndex("CREATE INDEX ON %s (values)");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 0, 0, {0})");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 1, 1, {0, 1})");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 2, 2, {0, 1, 2})");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (0, 3, 3, {0, 1, 2, 3})");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, value, values) VALUES (1, 0, 4, {0, 1, 2, 3, 4})");
|
||||
|
||||
flush(forceFlush);
|
||||
|
||||
assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",
|
||||
"UPDATE %s SET values= {6} WHERE partitionKey = ? AND clustering_1 = ? AND value = ?", 3, 3, 3);
|
||||
assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values",
|
||||
"UPDATE %s SET value= ? WHERE partitionKey = ? AND clustering_1 = ? AND values CONTAINS ?", 6, 3, 3, 3);
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering_1",
|
||||
"UPDATE %s SET values= {6} WHERE partitionKey = ? AND value = ?", 3, 3);
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering_1",
|
||||
"UPDATE %s SET value= ? WHERE partitionKey = ? AND values CONTAINS ?", 6, 3, 3);
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"UPDATE %s SET values= {6} WHERE clustering_1 = ?", 3);
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"UPDATE %s SET values= {6} WHERE value = ?", 3);
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"UPDATE %s SET value= ? WHERE values CONTAINS ?", 6, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithTwoClusteringColumns() throws Throwable
|
||||
{
|
||||
testUpdateWithTwoClusteringColumns(false);
|
||||
testUpdateWithTwoClusteringColumns(true);
|
||||
}
|
||||
|
||||
private void testUpdateWithTwoClusteringColumns(boolean forceFlush) throws Throwable
|
||||
{
|
||||
for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1, clustering_2))" + compactOption);
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 2, 2)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 3, 3)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 1, 1, 4)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 1, 2, 5)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (1, 0, 0, 6)");
|
||||
flush(forceFlush);
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 1, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 1, 1),
|
||||
row(7));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND (clustering_1, clustering_2) = (?, ?)", 8, 0, 1, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT value FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 1, 2),
|
||||
row(8));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey IN (?, ?) AND clustering_1 = ? AND clustering_2 = ?", 9, 0, 1, 0, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN (?, ?) AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 1, 0, 0),
|
||||
row(0, 0, 0, 9),
|
||||
row(1, 0, 0, 9));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey IN ? AND clustering_1 = ? AND clustering_2 = ?", 9, Arrays.asList(0, 1), 0, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey IN ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
Arrays.asList(0, 1), 0, 0),
|
||||
row(0, 0, 0, 9),
|
||||
row(1, 0, 0, 9));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 IN (?, ?)", 12, 0, 1, 1, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 IN (?, ?)",
|
||||
0, 1, 1, 2),
|
||||
row(0, 1, 1, 12),
|
||||
row(0, 1, 2, 12));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 IN (?, ?) AND clustering_2 IN (?, ?)", 10, 0, 1, 0, 1, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 IN (?, ?) AND clustering_2 IN (?, ?)",
|
||||
0, 1, 0, 1, 2),
|
||||
row(0, 0, 1, 10),
|
||||
row(0, 0, 2, 10),
|
||||
row(0, 1, 1, 10),
|
||||
row(0, 1, 2, 10));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND (clustering_1, clustering_2) IN ((?, ?), (?, ?))", 20, 0, 0, 2, 1, 2);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) IN ((?, ?), (?, ?))",
|
||||
0, 0, 2, 1, 2),
|
||||
row(0, 0, 2, 20),
|
||||
row(0, 1, 2, 20));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", null, 0, 0, 2);
|
||||
flush(forceFlush);
|
||||
|
||||
if (isEmpty(compactOption))
|
||||
{
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) IN ((?, ?), (?, ?))",
|
||||
0, 0, 2, 1, 2),
|
||||
row(0, 0, 2, null),
|
||||
row(0, 1, 2, 20));
|
||||
}
|
||||
else
|
||||
{
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND (clustering_1, clustering_2) IN ((?, ?), (?, ?))",
|
||||
0, 0, 2, 1, 2),
|
||||
row(0, 1, 2, 20));
|
||||
}
|
||||
|
||||
// test invalid queries
|
||||
|
||||
// missing primary key element
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey",
|
||||
"UPDATE %s SET value = ? WHERE clustering_1 = ? AND clustering_2 = ?", 7, 1, 1);
|
||||
|
||||
String errorMsg = isEmpty(compactOption) ? "Some clustering keys are missing: clustering_1"
|
||||
: "PRIMARY KEY column \"clustering_2\" cannot be restricted as preceding column \"clustering_1\" is not restricted";
|
||||
|
||||
assertInvalidMessage(errorMsg,
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_2 = ?", 7, 0, 1);
|
||||
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering_1, clustering_2",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ?", 7, 0);
|
||||
|
||||
// token function
|
||||
assertInvalidMessage("The token function cannot be used in WHERE clauses for UPDATE statements",
|
||||
"UPDATE %s SET value = ? WHERE token(partitionKey) = token(?) AND clustering_1 = ? AND clustering_2 = ?",
|
||||
7, 0, 1, 1);
|
||||
|
||||
// multiple time the same value
|
||||
assertInvalidSyntax("UPDATE %s SET value = ?, value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 1, 1);
|
||||
|
||||
// multiple time same primary key element in WHERE clause
|
||||
assertInvalidMessage("clustering_1 cannot be restricted by more than one relation if it includes an Equal",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ? AND clustering_1 = ?", 7, 0, 1, 1, 1);
|
||||
|
||||
// unknown identifiers
|
||||
assertInvalidMessage("Unknown identifier value1",
|
||||
"UPDATE %s SET value1 = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 1, 1);
|
||||
|
||||
assertInvalidMessage("Undefined name partitionkey1 in where clause ('partitionkey1 = ?')",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey1 = ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 1, 1);
|
||||
|
||||
assertInvalidMessage("Undefined name clustering_3 in where clause ('clustering_3 = ?')",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_3 = ?", 7, 0, 1, 1);
|
||||
|
||||
// Invalid operator in the where clause
|
||||
assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey > ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 1, 1);
|
||||
|
||||
assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey CONTAINS ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 1, 1);
|
||||
|
||||
String expectedMsg = isEmpty(compactOption) ? "Non PRIMARY KEY columns found in where clause: value"
|
||||
: "Predicates on the non-primary-key column (value) of a COMPACT table are not yet supported";
|
||||
assertInvalidMessage(expectedMsg,
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ? AND value = ?", 7, 0, 1, 1, 3);
|
||||
|
||||
assertInvalidMessage("Slice restrictions are not supported on the clustering columns in UPDATE statements",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND clustering_1 > ?", 7, 0, 1);
|
||||
|
||||
assertInvalidMessage("Slice restrictions are not supported on the clustering columns in UPDATE statements",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey = ? AND (clustering_1, clustering_2) > (?, ?)", 7, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithMultiplePartitionKeyComponents() throws Throwable
|
||||
{
|
||||
testUpdateWithMultiplePartitionKeyComponents(false);
|
||||
testUpdateWithMultiplePartitionKeyComponents(true);
|
||||
}
|
||||
|
||||
public void testUpdateWithMultiplePartitionKeyComponents(boolean forceFlush) throws Throwable
|
||||
{
|
||||
for (String compactOption : new String[] { "", " WITH COMPACT STORAGE" })
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey_1 int," +
|
||||
"partitionKey_2 int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
" PRIMARY KEY ((partitionKey_1, partitionKey_2), clustering_1, clustering_2))" + compactOption);
|
||||
|
||||
execute("INSERT INTO %s (partitionKey_1, partitionKey_2, clustering_1, clustering_2, value) VALUES (0, 0, 0, 0, 0)");
|
||||
execute("INSERT INTO %s (partitionKey_1, partitionKey_2, clustering_1, clustering_2, value) VALUES (0, 1, 0, 1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey_1, partitionKey_2, clustering_1, clustering_2, value) VALUES (0, 1, 1, 1, 2)");
|
||||
execute("INSERT INTO %s (partitionKey_1, partitionKey_2, clustering_1, clustering_2, value) VALUES (1, 0, 0, 1, 3)");
|
||||
execute("INSERT INTO %s (partitionKey_1, partitionKey_2, clustering_1, clustering_2, value) VALUES (1, 1, 0, 1, 3)");
|
||||
flush(forceFlush);
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey_1 = ? AND partitionKey_2 = ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 0, 0, 0);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT value FROM %s WHERE partitionKey_1 = ? AND partitionKey_2 = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 0, 0, 0),
|
||||
row(7));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey_1 IN (?, ?) AND partitionKey_2 = ? AND clustering_1 = ? AND clustering_2 = ?", 9, 0, 1, 1, 0, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey_1 IN (?, ?) AND partitionKey_2 = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 1, 1, 0, 1),
|
||||
row(0, 1, 0, 1, 9),
|
||||
row(1, 1, 0, 1, 9));
|
||||
|
||||
execute("UPDATE %s SET value = ? WHERE partitionKey_1 IN (?, ?) AND partitionKey_2 IN (?, ?) AND clustering_1 = ? AND clustering_2 = ?", 10, 0, 1, 0, 1, 0, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(0, 0, 0, 0, 7),
|
||||
row(0, 0, 0, 1, 10),
|
||||
row(0, 1, 0, 1, 10),
|
||||
row(0, 1, 1, 1, 2),
|
||||
row(1, 0, 0, 1, 10),
|
||||
row(1, 1, 0, 1, 10));
|
||||
|
||||
// missing primary key element
|
||||
assertInvalidMessage("Some partition key parts are missing: partitionkey_2",
|
||||
"UPDATE %s SET value = ? WHERE partitionKey_1 = ? AND clustering_1 = ? AND clustering_2 = ?", 7, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithAStaticColumn() throws Throwable
|
||||
{
|
||||
testUpdateWithAStaticColumn(false);
|
||||
testUpdateWithAStaticColumn(true);
|
||||
}
|
||||
|
||||
private void testUpdateWithAStaticColumn(boolean forceFlush) throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (partitionKey int," +
|
||||
"clustering_1 int," +
|
||||
"clustering_2 int," +
|
||||
"value int," +
|
||||
"staticValue text static," +
|
||||
" PRIMARY KEY (partitionKey, clustering_1, clustering_2))");
|
||||
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value, staticValue) VALUES (0, 0, 0, 0, 'A')");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value) VALUES (0, 0, 1, 1)");
|
||||
execute("INSERT INTO %s (partitionKey, clustering_1, clustering_2, value, staticValue) VALUES (1, 0, 0, 6, 'B')");
|
||||
flush(forceFlush);
|
||||
|
||||
execute("UPDATE %s SET staticValue = ? WHERE partitionKey = ?", "A2", 0);
|
||||
flush(forceFlush);
|
||||
|
||||
assertRows(execute("SELECT DISTINCT staticValue FROM %s WHERE partitionKey = ?", 0),
|
||||
row("A2"));
|
||||
|
||||
assertInvalidMessage("Some clustering keys are missing: clustering_1, clustering_2",
|
||||
"UPDATE %s SET staticValue = ?, value = ? WHERE partitionKey = ?", "A2", 7, 0);
|
||||
|
||||
execute("UPDATE %s SET staticValue = ?, value = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
"A3", 7, 0, 0, 1);
|
||||
flush(forceFlush);
|
||||
assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
0, 0, 1),
|
||||
row(0, 0, 1, "A3", 7));
|
||||
|
||||
assertInvalidMessage("Invalid restrictions on clustering columns since the UPDATE statement modifies only static columns",
|
||||
"UPDATE %s SET staticValue = ? WHERE partitionKey = ? AND clustering_1 = ? AND clustering_2 = ?",
|
||||
"A3", 0, 0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithStaticList() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (k int," +
|
||||
"clustering int," +
|
||||
"value int," +
|
||||
"l list<text> static," +
|
||||
" PRIMARY KEY (k, clustering))");
|
||||
|
||||
execute("INSERT INTO %s(k, clustering, value, l) VALUES (?, ?, ?, ?)", 0, 0, 0 ,list("v1", "v2", "v3"));
|
||||
|
||||
assertRows(execute("SELECT l FROM %s WHERE k = 0"), row(list("v1", "v2", "v3")));
|
||||
|
||||
execute("UPDATE %s SET l[?] = ? WHERE k = ?", 1, "v4", 0);
|
||||
|
||||
assertRows(execute("SELECT l FROM %s WHERE k = 0"), row(list("v1", "v4", "v3")));
|
||||
}
|
||||
|
||||
private void flush(boolean forceFlush)
|
||||
{
|
||||
if (forceFlush)
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ import com.google.common.base.Joiner;
|
|||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.apache.cassandra.Util;
|
||||
import org.apache.cassandra.db.*;
|
||||
import org.apache.cassandra.db.marshal.Int32Type;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
|
||||
|
|
@ -346,6 +344,201 @@ public class RangeTombstoneListTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonSortedAdditionTestWithOneTombstoneWithEmptyEnd()
|
||||
{
|
||||
nonSortedAdditionTestWithOneRangeWithEmptyEnd(0);
|
||||
nonSortedAdditionTestWithOneRangeWithEmptyEnd(10);
|
||||
}
|
||||
|
||||
private static void nonSortedAdditionTestWithOneRangeWithEmptyEnd(int initialCapacity)
|
||||
{
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, initialCapacity);
|
||||
RangeTombstone rt1 = rt(1, 5, 3);
|
||||
RangeTombstone rt2 = rt(7, 10, 2);
|
||||
RangeTombstone rt3 = atLeast(11, 1, 0);
|
||||
|
||||
l.add(rt2);
|
||||
l.add(rt3);
|
||||
l.add(rt1);
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(rt1, iter.next());
|
||||
assertRT(rt2, iter.next());
|
||||
assertRT(rt3, iter.next());
|
||||
|
||||
assert !iter.hasNext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRangeWithEmptyEndWitchIncludeExistingRange()
|
||||
{
|
||||
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
|
||||
l.add(rt(4, 10, 3));
|
||||
l.add(atLeast(3, 4, 0));
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(atLeast(3, 4, 0), iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRangeWithEmptyStartAndEnd()
|
||||
{
|
||||
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
|
||||
l.add(rt(4, 10, 3));
|
||||
l.add(atMost(12, 4, 0));
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(atMost(12, 4, 0), iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRangeWithEmptyEndToRangeWithEmptyStartAndEnd()
|
||||
{
|
||||
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
|
||||
l.add(new RangeTombstone(Slice.ALL, new DeletionTime(2, 0)));
|
||||
l.add(atLeast(12, 4, 0));
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(lessThan(12, 2, 0), iter.next());
|
||||
assertRT(atLeast(12, 4, 0), iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRangeWithEmptyEndWitchIncludeExistingRangeWithEmptyEnd()
|
||||
{
|
||||
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
|
||||
l.add(atLeast(5, 3, 0));
|
||||
l.add(atLeast(3, 4, 0));
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(atLeast(3, 4, 0), iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addIncludedRangeToRangeWithEmptyEnd()
|
||||
{
|
||||
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
|
||||
l.add(atLeast(3, 3, 0));
|
||||
l.add(rt(4, 10, 4));
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(rtie(3, 4, 3), iter.next());
|
||||
assertRT(rt(4, 10, 4), iter.next());
|
||||
assertRT(greaterThan(10, 3, 0), iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addIncludedRangeWithEmptyEndToRangeWithEmptyEnd()
|
||||
{
|
||||
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
|
||||
l.add(atLeast(3, 3, 0));
|
||||
l.add(atLeast(5, 4, 0));
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(rtie(3, 5, 3), iter.next());
|
||||
assertRT(atLeast(5, 4, 0), iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRangeWithEmptyEndWitchOverlapExistingRange()
|
||||
{
|
||||
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
|
||||
l.add(rt(4, 10, 3));
|
||||
l.add(atLeast(6, 4, 0));
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(rtie(4, 6, 3), iter.next());
|
||||
assertRT(atLeast(6, 4, 0), iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addOverlappingRangeToRangeWithEmptyEnd()
|
||||
{
|
||||
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
|
||||
l.add(atLeast(3, 3, 0));
|
||||
l.add(rt(1, 10, 4));
|
||||
|
||||
Iterator<RangeTombstone> iter = l.iterator();
|
||||
assertRT(rt(1, 10, 4), iter.next());
|
||||
assertRT(greaterThan(10, 3, 0), iter.next());
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchTestWithEmptyStart()
|
||||
{
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
l.add(atMost(4, 5, 0));
|
||||
l.add(rt(4, 6, 2));
|
||||
l.add(rt(9, 12, 1));
|
||||
l.add(rt(14, 15, 3));
|
||||
l.add(rt(15, 17, 6));
|
||||
|
||||
assertEquals(5, l.searchDeletionTime(clustering(-1)).markedForDeleteAt());
|
||||
assertEquals(5, l.searchDeletionTime(clustering(0)).markedForDeleteAt());
|
||||
assertEquals(5, l.searchDeletionTime(clustering(3)).markedForDeleteAt());
|
||||
assertEquals(5, l.searchDeletionTime(clustering(4)).markedForDeleteAt());
|
||||
|
||||
assertEquals(2, l.searchDeletionTime(clustering(5)).markedForDeleteAt());
|
||||
|
||||
assertEquals(null, l.searchDeletionTime(clustering(7)));
|
||||
|
||||
assertEquals(3, l.searchDeletionTime(clustering(14)).markedForDeleteAt());
|
||||
|
||||
assertEquals(6, l.searchDeletionTime(clustering(15)).markedForDeleteAt());
|
||||
assertEquals(null, l.searchDeletionTime(clustering(18)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchTestWithRangeWithEmptyEnd()
|
||||
{
|
||||
RangeTombstoneList l = new RangeTombstoneList(cmp, 0);
|
||||
l.add(rt(0, 4, 5));
|
||||
l.add(rt(4, 6, 2));
|
||||
l.add(rt(9, 12, 1));
|
||||
l.add(rt(14, 15, 3));
|
||||
l.add(atLeast(15, 6, 0));
|
||||
|
||||
assertEquals(null, l.searchDeletionTime(clustering(-1)));
|
||||
|
||||
assertEquals(5, l.searchDeletionTime(clustering(0)).markedForDeleteAt());
|
||||
assertEquals(5, l.searchDeletionTime(clustering(3)).markedForDeleteAt());
|
||||
assertEquals(5, l.searchDeletionTime(clustering(4)).markedForDeleteAt());
|
||||
|
||||
assertEquals(2, l.searchDeletionTime(clustering(5)).markedForDeleteAt());
|
||||
|
||||
assertEquals(null, l.searchDeletionTime(clustering(7)));
|
||||
|
||||
assertEquals(3, l.searchDeletionTime(clustering(14)).markedForDeleteAt());
|
||||
|
||||
assertEquals(6, l.searchDeletionTime(clustering(15)).markedForDeleteAt());
|
||||
assertEquals(6, l.searchDeletionTime(clustering(1000)).markedForDeleteAt());
|
||||
}
|
||||
|
||||
private static void assertRT(RangeTombstone expected, RangeTombstone actual)
|
||||
{
|
||||
assertTrue(String.format("%s != %s", toString(expected), toString(actual)), cmp.compare(expected.deletedSlice().start(), actual.deletedSlice().start()) == 0);
|
||||
|
|
@ -420,11 +613,6 @@ public class RangeTombstoneListTest
|
|||
return ByteBufferUtil.bytes(i);
|
||||
}
|
||||
|
||||
private static int i(Slice.Bound bound)
|
||||
{
|
||||
return ByteBufferUtil.toInt(bound.get(0));
|
||||
}
|
||||
|
||||
private static RangeTombstone rt(int start, int end, long tstamp)
|
||||
{
|
||||
return rt(start, end, tstamp, 0);
|
||||
|
|
@ -459,4 +647,24 @@ public class RangeTombstoneListTest
|
|||
{
|
||||
return new RangeTombstone(Slice.make(Slice.Bound.inclusiveStartOf(bb(start)), Slice.Bound.exclusiveEndOf(bb(end))), new DeletionTime(tstamp, delTime));
|
||||
}
|
||||
|
||||
private static RangeTombstone atLeast(int start, long tstamp, int delTime)
|
||||
{
|
||||
return new RangeTombstone(Slice.make(Slice.Bound.inclusiveStartOf(bb(start)), Slice.Bound.TOP), new DeletionTime(tstamp, delTime));
|
||||
}
|
||||
|
||||
private static RangeTombstone atMost(int end, long tstamp, int delTime)
|
||||
{
|
||||
return new RangeTombstone(Slice.make(Slice.Bound.BOTTOM, Slice.Bound.inclusiveEndOf(bb(end))), new DeletionTime(tstamp, delTime));
|
||||
}
|
||||
|
||||
private static RangeTombstone lessThan(int end, long tstamp, int delTime)
|
||||
{
|
||||
return new RangeTombstone(Slice.make(Slice.Bound.BOTTOM, Slice.Bound.exclusiveEndOf(bb(end))), new DeletionTime(tstamp, delTime));
|
||||
}
|
||||
|
||||
private static RangeTombstone greaterThan(int start, long tstamp, int delTime)
|
||||
{
|
||||
return new RangeTombstone(Slice.make(Slice.Bound.exclusiveStartOf(bb(start)), Slice.Bound.TOP), new DeletionTime(tstamp, delTime));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue