Merge branch cassandra-2.2 into cassandra-3.0
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/a8807391 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/a8807391 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/a8807391 Branch: refs/heads/trunk Commit: a880739187ea5521e8e558a19871985a846b330e Parents: fa909cc a337907 Author: blerer <[email protected]> Authored: Tue Oct 20 14:09:16 2015 +0200 Committer: blerer <[email protected]> Committed: Tue Oct 20 14:10:15 2015 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + pylib/cqlshlib/cql3handling.py | 4 ++-- .../org/apache/cassandra/cql3/Operations.java | 16 ++++++++++++- .../cql3/statements/DeleteStatement.java | 7 +++--- .../cql3/statements/ModificationStatement.java | 21 +++++++++------- .../cql3/statements/UpdateStatement.java | 25 ++++++++++---------- .../operations/InsertUpdateIfConditionTest.java | 11 +++++++++ 7 files changed, 57 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/a8807391/CHANGES.txt ---------------------------------------------------------------------- diff --cc CHANGES.txt index 5a118ca,261a53a..25ad1fb --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,34 -1,19 +1,35 @@@ -2.2.4 +3.0 +Merged from 2.2: * Expose phi values from failure detector via JMX and tweak debug and trace logging (CASSANDRA-9526) - * Fix RangeNamesQueryPager (CASSANDRA-10509) - * Deprecate Pig support (CASSANDRA-10542) - * Reduce contention getting instances of CompositeType (CASSANDRA-10433) Merged from 2.1: + * Fix conditions on static columns (CASSANDRA-10264) * AssertionError: attempted to delete non-existing file CommitLog (CASSANDRA-10377) - * (cqlsh) Distinguish negative and positive infinity in output (CASSANDRA-10523) - * (cqlsh) allow custom time_format for COPY TO (CASSANDRA-8970) - * Don't allow startup if the node's rack has changed (CASSANDRA-10242) - * (cqlsh) show partial trace if incomplete after max_trace_wait (CASSANDRA-7645) -2.2.3 +3.0-rc2 + * Fix SELECT DISTINCT queries between 2.2.2 nodes and 3.0 nodes (CASSANDRA-10473) + * Remove circular references in SegmentedFile (CASSANDRA-10543) + * Ensure validation of indexed values only occurs once per-partition (CASSANDRA-10536) + * Fix handling of static columns for range tombstones in thrift (CASSANDRA-10174) + * Support empty ColumnFilter for backward compatility on empty IN (CASSANDRA-10471) + * Remove Pig support (CASSANDRA-10542) + * Fix LogFile throws Exception when assertion is disabled (CASSANDRA-10522) + * Revert CASSANDRA-7486, make CMS default GC, move GC config to + conf/jvm.options (CASSANDRA-10403) + * Fix TeeingAppender causing some logs to be truncated/empty (CASSANDRA-10447) + * Allow EACH_QUORUM for reads (CASSANDRA-9602) + * Fix potential ClassCastException while upgrading (CASSANDRA-10468) + * Fix NPE in MVs on update (CASSANDRA-10503) + * Only include modified cell data in indexing deltas (CASSANDRA-10438) + * Do not load keyspace when creating sstable writer (CASSANDRA-10443) + * If node is not yet gossiping write all MV updates to batchlog only (CASSANDRA-10413) + * Re-populate token metadata after commit log recovery (CASSANDRA-10293) + * Provide additional metrics for materialized views (CASSANDRA-10323) + * Flush system schema tables after local schema changes (CASSANDRA-10429) +Merged from 2.2: + * Reduce contention getting instances of CompositeType (CASSANDRA-10433) + * Fix the regression when using LIMIT with aggregates (CASSANDRA-10487) * Avoid NoClassDefFoundError during DataDescriptor initialization on windows (CASSANDRA-10412) * Preserve case of quoted Role & User names (CASSANDRA-10394) * cqlsh pg-style-strings broken (CASSANDRA-10484) http://git-wip-us.apache.org/repos/asf/cassandra/blob/a8807391/pylib/cqlshlib/cql3handling.py ---------------------------------------------------------------------- diff --cc pylib/cqlshlib/cql3handling.py index 1398e0d,b4edac1..42e542f --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@@ -894,7 -874,7 +894,7 @@@ syntax_rules += r'' ; <conditions> ::= <condition> ( "AND" <condition> )* ; --<condition> ::= <cident> ( "[" <term> "]" )? ( ( "=" | "<" | ">" | "<=" | ">=" | "!=" ) <term> ++<condition> ::= <cident> ( "[" <term> "]" )? (("=" | "<" | ">" | "<=" | ">=" | "!=") <term> | "IN" "(" <term> ( "," <term> )* ")") ; ''' http://git-wip-us.apache.org/repos/asf/cassandra/blob/a8807391/src/java/org/apache/cassandra/cql3/Operations.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/Operations.java index c4cade1,0000000..0ef8517 mode 100644,000000..100644 --- a/src/java/org/apache/cassandra/cql3/Operations.java +++ b/src/java/org/apache/cassandra/cql3/Operations.java @@@ -1,135 -1,0 +1,149 @@@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +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 org.apache.cassandra.cql3.statements.StatementType; + +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 type of statement. ++ */ ++ private final StatementType type; ++ ++ /** + * The operations on regular columns. + */ + private final List<Operation> regularOperations = new ArrayList<>(); + + /** + * The operations on static columns. + */ + private final List<Operation> staticOperations = new ArrayList<>(); + ++ public Operations(StatementType type) ++ { ++ this.type = type; ++ } ++ + /** + * 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(); ++ // If we have regular operations, this applies to regular columns. ++ // Otherwise, if the statement is a DELETE and staticOperations is also empty, this means we have no operations, ++ // which for a DELETE means a full row deletion. Which means the operation applies to all columns and regular ones in particular. ++ return !regularOperations.isEmpty() || (type.isDelete() && staticOperations.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; + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/a8807391/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java index d51f261,ff685cf..0efe35c --- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java @@@ -119,19 -116,14 +119,19 @@@ public class DeleteStatement extends Mo List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions, boolean ifExists) { -- super(name, attrs, conditions, false, ifExists); ++ super(name, StatementType.DELETE, attrs, conditions, false, ifExists); this.deletions = deletions; 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) { - Operations operations = new Operations(); - DeleteStatement stmt = new DeleteStatement(ModificationStatement.StatementType.DELETE, boundNames.size(), cfm, attrs); ++ Operations operations = new Operations(type); for (Operation.RawDeletion deletion : deletions) { @@@ -143,27 -139,10 +143,26 @@@ Operation op = deletion.prepare(cfm.ksName, def); op.collectMarkerSpecification(boundNames); - stmt.addOperation(op); + operations.add(op); } - StatementRestrictions restrictions = newRestrictions(StatementType.DELETE, - cfm, - stmt.processWhereClause(whereClause, boundNames); ++ StatementRestrictions restrictions = newRestrictions(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; } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/a8807391/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index 0e989e6,8b594dd..1ea1e4d --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@@ -734,14 -748,14 +734,21 @@@ public abstract class ModificationState public static abstract class Parsed extends CFStatement { - protected final Attributes.Raw attrs; - protected final List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions; ++ protected final StatementType type; + private final Attributes.Raw attrs; + private final List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions; private final boolean ifNotExists; private final boolean ifExists; -- protected Parsed(CFName name, Attributes.Raw attrs, List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions, boolean ifNotExists, boolean ifExists) ++ protected Parsed(CFName name, ++ StatementType type, ++ Attributes.Raw attrs, ++ List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions, ++ boolean ifNotExists, ++ boolean ifExists) { super(name); ++ this.type = type; this.attrs = attrs; this.conditions = conditions == null ? Collections.<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>>emptyList() : conditions; this.ifNotExists = ifNotExists; @@@ -763,114 -777,59 +770,112 @@@ Attributes preparedAttributes = attrs.prepare(keyspace(), columnFamily()); preparedAttributes.collectMarkerSpecification(boundNames); - ModificationStatement stmt = prepareInternal(metadata, boundNames, preparedAttributes); + Conditions preparedConditions = prepareConditions(metadata, boundNames); - if (ifNotExists || ifExists || !conditions.isEmpty()) + return prepareInternal(metadata, + boundNames, + preparedConditions, + preparedAttributes); + } + + /** + * Returns the column conditions. + * + * @param metadata the column family meta data + * @param boundNames the bound names + * @return the column conditions. + */ + private Conditions prepareConditions(CFMetaData metadata, VariableSpecifications boundNames) + { + // To have both 'IF EXISTS'/'IF NOT EXISTS' and some other conditions doesn't make sense. + // So far this is enforced by the parser, but let's assert it for sanity if ever the parse changes. + if (ifExists) { - if (stmt.isCounter()) - throw new InvalidRequestException("Conditional updates are not supported on counter tables"); + assert conditions.isEmpty(); + assert !ifNotExists; + return Conditions.IF_EXISTS_CONDITION; + } - if (attrs.timestamp != null) - throw new InvalidRequestException("Cannot provide custom timestamp for conditional updates"); + if (ifNotExists) + { + assert conditions.isEmpty(); + assert !ifExists; + return Conditions.IF_NOT_EXISTS_CONDITION; + } - if (ifNotExists) - { - // To have both 'IF NOT EXISTS' and some other conditions doesn't make sense. - // So far this is enforced by the parser, but let's assert it for sanity if ever the parse changes. - assert conditions.isEmpty(); - assert !ifExists; - stmt.setIfNotExistCondition(); - } - else if (ifExists) - { - assert conditions.isEmpty(); - assert !ifNotExists; - stmt.setIfExistCondition(); - } - else - { - for (Pair<ColumnIdentifier.Raw, ColumnCondition.Raw> entry : conditions) - { - ColumnIdentifier id = entry.left.prepare(metadata); - ColumnDefinition def = metadata.getColumnDefinition(id); - if (def == null) - throw new InvalidRequestException(String.format("Unknown identifier %s", id)); - - ColumnCondition condition = entry.right.prepare(keyspace(), def); - condition.collectMarkerSpecification(boundNames); - - switch (def.kind) - { - case PARTITION_KEY: - case CLUSTERING_COLUMN: - throw new InvalidRequestException(String.format("PRIMARY KEY column '%s' cannot have IF conditions", id)); - default: - stmt.addCondition(condition); - break; - } - } - } + if (conditions.isEmpty()) + return Conditions.EMPTY_CONDITION; + + return prepareColumnConditions(metadata, boundNames); + } + + /** + * Returns the column conditions. + * + * @param metadata the column family meta data + * @param boundNames the bound names + * @return the column conditions. + */ + private ColumnConditions prepareColumnConditions(CFMetaData metadata, VariableSpecifications boundNames) + { + checkNull(attrs.timestamp, "Cannot provide custom timestamp for conditional updates"); + + ColumnConditions.Builder builder = ColumnConditions.newBuilder(); + + for (Pair<ColumnIdentifier.Raw, ColumnCondition.Raw> entry : conditions) + { + ColumnIdentifier id = entry.left.prepare(metadata); + ColumnDefinition def = metadata.getColumnDefinition(id); + checkNotNull(metadata.getColumnDefinition(id), "Unknown identifier %s in IF conditions", id); + + ColumnCondition condition = entry.right.prepare(keyspace(), def); + condition.collectMarkerSpecification(boundNames); - stmt.validateWhereClauseForConditions(); + checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY column '%s' cannot have IF conditions", id); + builder.add(condition); } - return stmt; + return builder.build(); } - protected abstract ModificationStatement prepareInternal(CFMetaData cfm, VariableSpecifications boundNames, Attributes attrs) throws InvalidRequestException; + protected abstract ModificationStatement prepareInternal(CFMetaData cfm, + VariableSpecifications boundNames, + Conditions conditions, + Attributes attrs); + + /** + * Creates the restrictions. + * - * @param type the statement type + * @param cfm the column family meta data + * @param boundNames the bound names + * @param operations the column operations + * @param where the where clause + * @param conditions the conditions + * @return the restrictions + */ - protected static StatementRestrictions newRestrictions(StatementType type, - CFMetaData cfm, - VariableSpecifications boundNames, - Operations operations, - WhereClause where, - Conditions conditions) ++ protected StatementRestrictions newRestrictions(CFMetaData cfm, ++ VariableSpecifications boundNames, ++ Operations operations, ++ WhereClause where, ++ Conditions conditions) + { + if (where.containsCustomExpressions()) + throw new InvalidRequestException(CUSTOM_EXPRESSIONS_NOT_ALLOWED); + + boolean applyOnlyToStaticColumns = appliesOnlyToStaticColumns(operations, conditions); + return new StatementRestrictions(type, cfm, where, boundNames, applyOnlyToStaticColumns, false, false, false); + } + + /** + * Retrieves the <code>ColumnDefinition</code> corresponding to the specified raw <code>ColumnIdentifier</code>. + * + * @param cfm the column family meta data + * @param rawId the raw <code>ColumnIdentifier</code> + * @return the <code>ColumnDefinition</code> corresponding to the specified raw <code>ColumnIdentifier</code> + */ + protected static ColumnDefinition getColumnDefinition(CFMetaData cfm, Raw rawId) + { + ColumnIdentifier id = rawId.prepare(cfm); + return checkNotNull(cfm.getColumnDefinition(id), "Unknown identifier %s", id); + } } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/a8807391/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java index d6d0266,ad46a0f..6f872d4 --- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java @@@ -131,42 -166,47 +131,42 @@@ public class UpdateStatement extends Mo List<Term.Raw> columnValues, boolean ifNotExists) { -- super(name, attrs, null, ifNotExists, false); ++ super(name, StatementType.INSERT, attrs, null, ifNotExists, false); this.columnNames = columnNames; 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"); + + WhereClause.Builder whereClause = new WhereClause.Builder(); - Operations operations = new Operations(); ++ Operations operations = new Operations(type); + 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); + whereClause.add(new SingleColumnRelation(columnNames.get(i), Operator.EQ, value)); } else { @@@ -176,24 -216,7 +176,24 @@@ } } - return stmt; + boolean applyOnlyToStaticColumns = appliesOnlyToStaticColumns(operations, conditions) && !hasClusteringColumnsSet; + - StatementRestrictions restrictions = new StatementRestrictions(StatementType.INSERT, ++ StatementRestrictions restrictions = new StatementRestrictions(type, + cfm, + whereClause.build(), + boundNames, + applyOnlyToStaticColumns, + false, + false, + false); + - return new UpdateStatement(StatementType.INSERT, ++ return new UpdateStatement(type, + boundNames.size(), + cfm, + operations, + restrictions, + conditions, + attrs); } } @@@ -206,7 -229,7 +206,7 @@@ public ParsedInsertJson(CFName name, Attributes.Raw attrs, Json.Raw jsonValue, boolean ifNotExists) { -- super(name, attrs, null, ifNotExists, false); ++ super(name, StatementType.INSERT, attrs, null, ifNotExists, false); this.jsonValue = jsonValue; } @@@ -221,48 -242,15 +221,48 @@@ Collection<ColumnDefinition> defs = cfm.allColumns(); Json.Prepared prepared = jsonValue.prepareAndCollectMarkers(cfm, defs, boundNames); + WhereClause.Builder whereClause = new WhereClause.Builder(); - Operations operations = new Operations(); ++ Operations operations = new Operations(type); + 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)); + { + whereClause.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, ++ StatementRestrictions restrictions = new StatementRestrictions(type, + cfm, + whereClause.build(), + boundNames, + applyOnlyToStaticColumns, + false, + false, + false); + - return new UpdateStatement(StatementType.INSERT, ++ return new UpdateStatement(type, + boundNames.size(), + cfm, + operations, + restrictions, + conditions, + attrs); } } @@@ -289,18 -277,14 +289,18 @@@ List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions, boolean ifExists) { -- super(name, attrs, conditions, false, ifExists); ++ super(name, StatementType.UPDATE, attrs, conditions, false, ifExists); this.updates = updates; 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) { - Operations operations = new Operations(); - UpdateStatement stmt = new UpdateStatement(ModificationStatement.StatementType.UPDATE, boundNames.size(), cfm, attrs); ++ Operations operations = new Operations(type); for (Pair<ColumnIdentifier.Raw, Operation.RawUpdate> entry : updates) { @@@ -310,23 -294,20 +310,22 @@@ Operation operation = entry.right.prepare(keyspace(), def); operation.collectMarkerSpecification(boundNames); - - switch (def.kind) - { - case PARTITION_KEY: - case CLUSTERING_COLUMN: - throw new InvalidRequestException(String.format("PRIMARY KEY part %s found in SET part", entry.left)); - default: - stmt.addOperation(operation); - break; - } + operations.add(operation); } - StatementRestrictions restrictions = newRestrictions(StatementType.UPDATE, - cfm, - stmt.processWhereClause(whereClause, boundNames); - return stmt; ++ StatementRestrictions restrictions = newRestrictions(cfm, + boundNames, + operations, + whereClause, + conditions); + - return new UpdateStatement(StatementType.UPDATE, ++ return new UpdateStatement(type, + boundNames.size(), + cfm, + operations, + restrictions, + conditions, + attrs); } } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/a8807391/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java ---------------------------------------------------------------------- diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java index 9cde6d7,b73ecdf..ade80bb --- a/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java @@@ -188,25 -175,22 +188,36 @@@ public class InsertUpdateIfConditionTes 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"); + + createTable("CREATE TABLE %s(k int, s int static, i int, v text, PRIMARY KEY(k, i))"); + execute("INSERT INTO %s (k, s, i, v) VALUES ( 1, 1, 2, '1')"); + assertRows(execute("DELETE v FROM %s WHERE k = 1 AND i = 2 IF s != 1"), row(false, 1)); + assertRows(execute("DELETE v FROM %s WHERE k = 1 AND i = 2 IF s = 1"), row(true)); + assertRows(execute("SELECT * FROM %s WHERE k = 1 AND i = 2"), row(1, 2, 1, null)); + + assertRows(execute("DELETE FROM %s WHERE k = 1 AND i = 2 IF s != 1"), row(false, 1)); + assertRows(execute("DELETE FROM %s WHERE k = 1 AND i = 2 IF s = 1"), row(true)); + assertEmpty(execute("SELECT * FROM %s WHERE k = 1 AND i = 2")); + assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, null, 1, null)); } /**
