Return-Path: X-Original-To: apmail-cassandra-commits-archive@www.apache.org Delivered-To: apmail-cassandra-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 6A4F5E999 for ; Wed, 21 Nov 2012 08:14:14 +0000 (UTC) Received: (qmail 73889 invoked by uid 500); 21 Nov 2012 08:14:14 -0000 Delivered-To: apmail-cassandra-commits-archive@cassandra.apache.org Received: (qmail 73638 invoked by uid 500); 21 Nov 2012 08:14:14 -0000 Mailing-List: contact commits-help@cassandra.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cassandra.apache.org Delivered-To: mailing list commits@cassandra.apache.org Received: (qmail 73612 invoked by uid 99); 21 Nov 2012 08:14:13 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 21 Nov 2012 08:14:13 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 3927D31873C; Wed, 21 Nov 2012 08:14:13 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: slebresne@apache.org To: commits@cassandra.apache.org X-Mailer: ASF-Git Admin Mailer Subject: [2/2] git commit: Correctly handle prepared operation on collections Message-Id: <20121121081413.3927D31873C@tyr.zones.apache.org> Date: Wed, 21 Nov 2012 08:14:13 +0000 (UTC) Correctly handle prepared operation on collections patch by slebresne; reviewed by jbellis for CASSANDRA-4945 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b4f2f201 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b4f2f201 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b4f2f201 Branch: refs/heads/cassandra-1.2 Commit: b4f2f2014e088b293fc7305f9c3e6409ee428495 Parents: 553b9e8 Author: Sylvain Lebresne Authored: Wed Nov 21 09:13:07 2012 +0100 Committer: Sylvain Lebresne Committed: Wed Nov 21 09:13:07 2012 +0100 ---------------------------------------------------------------------- CHANGES.txt | 1 + src/java/org/apache/cassandra/cql3/Cql.g | 39 +++-- .../cassandra/cql3/operations/ColumnOperation.java | 27 +--- .../cassandra/cql3/operations/ListOperation.java | 83 +++++++-- .../cassandra/cql3/operations/MapOperation.java | 24 ++- .../cassandra/cql3/operations/Operation.java | 9 +- .../cql3/operations/PreparedOperation.java | 143 +++++++++++++++ .../cassandra/cql3/operations/SetOperation.java | 44 ++++- .../cassandra/cql3/statements/UpdateStatement.java | 25 +-- 9 files changed, 298 insertions(+), 97 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index e63350b..cdd651a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -17,6 +17,7 @@ * Use Stats.db when bulk loading if present (CASSANDRA-4957) * Skip repair on system_trace and keyspaces with RF=1 (CASSANDRA-4956) * Remove select arbitrary limit (CASSANDRA-4918) + * Correctly handle prepared operation on collections (CASSANDRA-4945) Merged from 1.1: * add basic authentication support for Pig CassandraStorage (CASSANDRA-3042) * fix CQL2 ALTER TABLE compaction_strategy_class altering (CASSANDRA-4965) http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/Cql.g ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g index ce08e66..9c3f77b 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -74,7 +74,7 @@ options { // used by UPDATE of the counter columns to validate if '-' was supplied by user public void validateMinusSupplied(Object op, final Term value, IntStream stream) throws MissingTokenException { - if (op == null && (value.isBindMarker() || Long.parseLong(value.getText()) > 0)) + if (op == null && Long.parseLong(value.getText()) > 0) throw new MissingTokenException(102, stream, value); } @@ -561,7 +561,8 @@ cfOrKsName[CFName name, boolean isKs] ; set_operation returns [Operation op] - : t=term { $op = ColumnOperation.Set(t); } + : t=finalTerm { $op = ColumnOperation.Set(t); } + | mk=QMARK { $op = new PreparedOperation(new Term($mk.text, $mk.type, ++currentBindMarkerIdx), PreparedOperation.Kind.SET); } | m=map_literal { $op = MapOperation.Set(m); } | l=list_literal { $op = ListOperation.Set(l); } | s=set_literal { $op = SetOperation.Set(s); } @@ -591,14 +592,9 @@ term returns [Term term] | t=QMARK { $term = new Term($t.text, $t.type, ++currentBindMarkerIdx); } ; -intTerm returns [Term integer] - : t=INTEGER { $integer = new Term($t.text, $t.type); } - | t=QMARK { $integer = new Term($t.text, $t.type, ++currentBindMarkerIdx); } - ; - termPairWithOperation[List> columns] : key=cident '=' - (set_op = set_operation { columns.add(Pair.create(key, set_op)); } + ( set_op=set_operation { columns.add(Pair.create(key, set_op)); } | c=cident op=operation { if (!key.equals(c)) @@ -611,6 +607,13 @@ termPairWithOperation[List> columns] addRecognitionError("Only expressions like X = + X are supported."); columns.add(Pair.create(key, ListOperation.Prepend(ll))); } + | mk=QMARK '+' c=cident + { + if (!key.equals(c)) + addRecognitionError("Only expressions like X = + X are supported."); + PreparedOperation pop = new PreparedOperation(new Term($mk.text, $mk.type, ++currentBindMarkerIdx), PreparedOperation.Kind.PREPARED_PLUS); + columns.add(Pair.create(key, pop)); + } ) | key=cident '[' t=term ']' '=' vv=term { @@ -622,15 +625,25 @@ termPairWithOperation[List> columns] } ; +intTerm returns [Term integer] + : t=INTEGER { $integer = new Term($t.text, $t.type); } + | t=QMARK { $integer = new Term($t.text, $t.type, ++currentBindMarkerIdx); } + ; + + operation returns [Operation op] - : '+' v=intTerm { $op = ColumnOperation.CounterInc(v); } - | sign='-'? v=intTerm + : '+' i=INTEGER { $op = ColumnOperation.CounterInc(new Term($i.text, $i.type)); } + | sign='-'? i=INTEGER { - validateMinusSupplied(sign, v, input); + Term t = new Term($i.text, $i.type); + validateMinusSupplied(sign, t, input); if (sign == null) - v = new Term(-(Long.valueOf(v.getText())), v.getType()); - $op = ColumnOperation.CounterDec(v); + t = new Term(-(Long.valueOf(t.getText())), t.getType()); + $op = ColumnOperation.CounterDec(t); } + | '+' mk=QMARK { $op = new PreparedOperation(new Term($mk.text, $mk.type, ++currentBindMarkerIdx), PreparedOperation.Kind.PLUS_PREPARED); } + | '-' mk=QMARK { $op = new PreparedOperation(new Term($mk.text, $mk.type, ++currentBindMarkerIdx), PreparedOperation.Kind.MINUS_PREPARED); } + | '+' ll=list_literal { $op = ListOperation.Append(ll); } | '-' ll=list_literal { $op = ListOperation.Discard(ll); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java b/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java index 0f4c1fc..bfdec8c 100644 --- a/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java +++ b/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java @@ -62,7 +62,8 @@ public class ColumnOperation implements Operation public void execute(ColumnFamily cf, ColumnNameBuilder builder, AbstractType validator, - UpdateParameters params) throws InvalidRequestException + UpdateParameters params, + List> list) throws InvalidRequestException { switch (kind) { @@ -78,28 +79,6 @@ public class ColumnOperation implements Operation } } - public void execute(ColumnFamily cf, ColumnNameBuilder builder, CollectionType validator, UpdateParameters params, List> list) throws InvalidRequestException - { - throw new InvalidRequestException("Column operations are only supported on simple types, but " + validator + " given."); - } - - public void executePreparedCollection(ColumnFamily cf, ColumnNameBuilder builder, CollectionType validator, UpdateParameters params) throws InvalidRequestException - { - - switch (validator.kind) - { - case LIST: - ListOperation.doInsertFromPrepared(cf, builder, (ListType)validator, value, params); - break; - case SET: - SetOperation.doInsertFromPrepared(cf, builder, (SetType)validator, value, params); - break; - case MAP: - MapOperation.doInsertFromPrepared(cf, builder, (MapType)validator, value, params); - break; - } - } - protected void doSet(ColumnFamily cf, ColumnNameBuilder builder, AbstractType validator, UpdateParameters params) throws InvalidRequestException { ByteBuffer colName = builder.build(); @@ -138,7 +117,7 @@ public class ColumnOperation implements Operation return Collections.singletonList(value); } - public boolean requiresRead() + public boolean requiresRead(AbstractType validator) { return false; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/ListOperation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/operations/ListOperation.java b/src/java/org/apache/cassandra/cql3/operations/ListOperation.java index 44d997e..25c616d 100644 --- a/src/java/org/apache/cassandra/cql3/operations/ListOperation.java +++ b/src/java/org/apache/cassandra/cql3/operations/ListOperation.java @@ -93,30 +93,30 @@ public class ListOperation implements Operation public void execute(ColumnFamily cf, ColumnNameBuilder builder, - CollectionType validator, + AbstractType validator, UpdateParameters params, List> list) throws InvalidRequestException { - if (validator.kind != CollectionType.Kind.LIST) + if (!(validator instanceof ListType)) throw new InvalidRequestException("List operations are only supported on List typed columns, but " + validator + " given."); switch (kind) { case SET: cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange())); - doAppend(cf, builder, validator, params); + doAppend(cf, builder, (CollectionType)validator, params); break; case SET_IDX: - doSet(cf, builder, params, validator, list); + doSet(cf, builder, params, (CollectionType)validator, list); break; case APPEND: - doAppend(cf, builder, validator, params); + doAppend(cf, builder, (CollectionType)validator, params); break; case PREPEND: - doPrepend(cf, builder, validator, params); + doPrepend(cf, builder, (CollectionType)validator, params); break; case DISCARD: - doDiscard(cf, validator, params, list); + doDiscard(cf, (CollectionType)validator, params, list); break; case DISCARD_IDX: doDiscardIdx(cf, params, list); @@ -126,18 +126,20 @@ public class ListOperation implements Operation } } - public void execute(ColumnFamily cf, ColumnNameBuilder builder, AbstractType validator, UpdateParameters params) throws InvalidRequestException + public static void doSetFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, ListType validator, Term values, UpdateParameters params) throws InvalidRequestException { - throw new InvalidRequestException("List operations are only supported on List typed columns, but " + validator + " given."); + if (!values.isBindMarker()) + throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); + + cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange())); + doAppendFromPrepared(cf, builder, validator, values, params); } - public static void doInsertFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, ListType validator, Term values, UpdateParameters params) throws InvalidRequestException + public static void doAppendFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, ListType validator, Term values, UpdateParameters params) throws InvalidRequestException { if (!values.isBindMarker()) throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); - cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange())); - try { List l = validator.compose(params.variables.get(values.bindIndex)); @@ -156,6 +158,61 @@ public class ListOperation implements Operation } } + public static void doPrependFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, ListType validator, Term values, UpdateParameters params) throws InvalidRequestException + { + if (!values.isBindMarker()) + throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); + + long time = REFERENCE_TIME - (System.currentTimeMillis() - REFERENCE_TIME); + + try + { + List l = validator.compose(params.variables.get(values.bindIndex)); + + for (int i = 0; i < l.size(); i++) + { + ColumnNameBuilder b = i == l.size() - 1 ? builder : builder.copy(); + PrecisionTime pt = getNextTime(time); + ByteBuffer uuid = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes(pt.millis, pt.nanos)); + ByteBuffer name = b.add(uuid).build(); + cf.addColumn(params.makeColumn(name, validator.valueComparator().decompose(l.get(i)))); + } + } + catch (MarshalException e) + { + throw new InvalidRequestException(e.getMessage()); + } + } + + public static void doDiscardFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, ListType validator, Term values, UpdateParameters params, List> list) throws InvalidRequestException + { + if (!values.isBindMarker()) + throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); + + if (list == null) + return; + + try + { + List l = validator.compose(params.variables.get(values.bindIndex)); + + Set toDiscard = new HashSet(); + for (Object elt : l) + toDiscard.add(validator.valueComparator().decompose(elt)); + + for (Pair p : list) + { + IColumn c = p.right; + if (toDiscard.contains(c.value())) + cf.addColumn(params.makeTombstone(c.name())); + } + } + catch (MarshalException e) + { + throw new InvalidRequestException(e.getMessage()); + } + } + private void doSet(ColumnFamily cf, ColumnNameBuilder builder, UpdateParameters params, CollectionType validator, List> list) throws InvalidRequestException { int idx = validateListIdx(values.get(0), list); @@ -219,7 +276,7 @@ public class ListOperation implements Operation return values; } - public boolean requiresRead() + public boolean requiresRead(AbstractType validator) { return kind == Kind.DISCARD || kind == Kind.DISCARD_IDX || kind == Kind.SET_IDX; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/MapOperation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/operations/MapOperation.java b/src/java/org/apache/cassandra/cql3/operations/MapOperation.java index 39a5413..5f844a7 100644 --- a/src/java/org/apache/cassandra/cql3/operations/MapOperation.java +++ b/src/java/org/apache/cassandra/cql3/operations/MapOperation.java @@ -57,18 +57,13 @@ public class MapOperation implements Operation this.kind = Kind.DISCARD; } - public void execute(ColumnFamily cf, ColumnNameBuilder builder, AbstractType validator, UpdateParameters params) throws InvalidRequestException - { - throw new InvalidRequestException("Map operations are only supported on Map typed columns, but " + validator + " given."); - } - public void execute(ColumnFamily cf, ColumnNameBuilder builder, - CollectionType validator, + AbstractType validator, UpdateParameters params, List> list) throws InvalidRequestException { - if (validator.kind != CollectionType.Kind.MAP) + if (!(validator instanceof MapType)) throw new InvalidRequestException("Map operations are only supported on Map typed columns, but " + validator + " given."); switch (kind) @@ -76,22 +71,29 @@ public class MapOperation implements Operation case SET: // fallthrough on purpose; remove previous Map before setting (PUT) the new one cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange())); case PUT: - doPut(cf, builder, validator, params); + doPut(cf, builder, (CollectionType)validator, params); break; case DISCARD: - doDiscard(cf, builder, validator, params); + doDiscard(cf, builder, (CollectionType)validator, params); break; default: throw new AssertionError("Unsupported Map operation: " + kind); } } - public static void doInsertFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, MapType validator, Term values, UpdateParameters params) throws InvalidRequestException + public static void doSetFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, MapType validator, Term values, UpdateParameters params) throws InvalidRequestException { if (!values.isBindMarker()) throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange())); + doPutFromPrepared(cf, builder, validator, values, params); + } + + public static void doPutFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, MapType validator, Term values, UpdateParameters params) throws InvalidRequestException + { + if (!values.isBindMarker()) + throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); try { @@ -136,7 +138,7 @@ public class MapOperation implements Operation return l; } - public boolean requiresRead() + public boolean requiresRead(AbstractType validator) { return kind == Kind.SET || kind == Kind.DISCARD; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/Operation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/operations/Operation.java b/src/java/org/apache/cassandra/cql3/operations/Operation.java index 5261677..28caea7 100644 --- a/src/java/org/apache/cassandra/cql3/operations/Operation.java +++ b/src/java/org/apache/cassandra/cql3/operations/Operation.java @@ -32,22 +32,17 @@ import org.apache.cassandra.utils.Pair; public interface Operation { - public static enum Type { COLUMN, COUNTER, LIST, SET, MAP } + public static enum Type { COLUMN, COUNTER, LIST, SET, MAP, PREPARED } public void execute(ColumnFamily cf, ColumnNameBuilder builder, AbstractType validator, - UpdateParameters params) throws InvalidRequestException; - - public void execute(ColumnFamily cf, - ColumnNameBuilder builder, - CollectionType validator, UpdateParameters params, List> list) throws InvalidRequestException; public List getValues(); - public boolean requiresRead(); + public boolean requiresRead(AbstractType validator); public Type getType(); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/PreparedOperation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/operations/PreparedOperation.java b/src/java/org/apache/cassandra/cql3/operations/PreparedOperation.java new file mode 100644 index 0000000..a54b065 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/operations/PreparedOperation.java @@ -0,0 +1,143 @@ +/** + * 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.operations; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; + +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.db.ColumnFamily; +import org.apache.cassandra.db.IColumn; +import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.db.marshal.CollectionType; +import org.apache.cassandra.db.marshal.ListType; +import org.apache.cassandra.db.marshal.MapType; +import org.apache.cassandra.db.marshal.SetType; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.utils.Pair; + +public class PreparedOperation implements Operation +{ + public enum Kind { SET, PREPARED_PLUS, PLUS_PREPARED, MINUS_PREPARED } + + private final Term preparedValue; + private final Kind kind; + + public PreparedOperation(Term value, Kind kind) + { + assert value.isBindMarker(); + this.preparedValue = value; + this.kind = kind; + } + + public void execute(ColumnFamily cf, + ColumnNameBuilder builder, + AbstractType validator, + UpdateParameters params, + List> list) throws InvalidRequestException + { + if (validator instanceof CollectionType) + { + switch (((CollectionType)validator).kind) + { + case LIST: + switch (kind) + { + case SET: + ListOperation.doSetFromPrepared(cf, builder, (ListType)validator, preparedValue, params); + break; + case PREPARED_PLUS: + ListOperation.doPrependFromPrepared(cf, builder, (ListType)validator, preparedValue, params); + break; + case PLUS_PREPARED: + ListOperation.doAppendFromPrepared(cf, builder, (ListType)validator, preparedValue, params); + break; + case MINUS_PREPARED: + ListOperation.doDiscardFromPrepared(cf, builder, (ListType)validator, preparedValue, params, list); + break; + } + break; + case SET: + switch (kind) + { + case SET: + SetOperation.doSetFromPrepared(cf, builder, (SetType)validator, preparedValue, params); + break; + case PREPARED_PLUS: + throw new InvalidRequestException("Unsupported syntax, cannot add to a prepared set"); + case PLUS_PREPARED: + SetOperation.doAddFromPrepared(cf, builder, (SetType)validator, preparedValue, params); + break; + case MINUS_PREPARED: + SetOperation.doDiscardFromPrepared(cf, builder, (SetType)validator, preparedValue, params); + break; + } + break; + case MAP: + switch (kind) + { + case SET: + MapOperation.doSetFromPrepared(cf, builder, (MapType)validator, preparedValue, params); + break; + case PREPARED_PLUS: + throw new InvalidRequestException("Unsupported syntax, cannot put to a prepared map"); + case PLUS_PREPARED: + MapOperation.doPutFromPrepared(cf, builder, (MapType)validator, preparedValue, params); + break; + case MINUS_PREPARED: + throw new InvalidRequestException("Unsuppoted syntax, discard syntax for map not supported"); + } + break; + } + } + else + { + switch (kind) + { + case SET: + ColumnOperation.Set(preparedValue).execute(cf, builder, validator, params, null); + break; + case PREPARED_PLUS: + throw new InvalidRequestException("Unsupported syntax for increment, must be of the form X = X + "); + case PLUS_PREPARED: + ColumnOperation.CounterInc(preparedValue).execute(cf, builder, validator, params, null); + break; + case MINUS_PREPARED: + ColumnOperation.CounterDec(preparedValue).execute(cf, builder, validator, params, null); + break; + } + } + } + + public List getValues() + { + return Collections.singletonList(preparedValue); + } + + public boolean requiresRead(AbstractType validator) + { + // Only prepared operation requiring a read is list discard + return (validator instanceof ListType) && kind == Kind.MINUS_PREPARED; + } + + public Type getType() + { + return Type.PREPARED; + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/SetOperation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/operations/SetOperation.java b/src/java/org/apache/cassandra/cql3/operations/SetOperation.java index 36330f7..5d63bd9 100644 --- a/src/java/org/apache/cassandra/cql3/operations/SetOperation.java +++ b/src/java/org/apache/cassandra/cql3/operations/SetOperation.java @@ -50,11 +50,11 @@ public class SetOperation implements Operation public void execute(ColumnFamily cf, ColumnNameBuilder builder, - CollectionType validator, + AbstractType validator, UpdateParameters params, List> list) throws InvalidRequestException { - if (validator.kind != CollectionType.Kind.SET) + if (!(validator instanceof SetType)) throw new InvalidRequestException("Set operations are only supported on Set typed columns, but " + validator + " given."); switch (kind) @@ -62,28 +62,30 @@ public class SetOperation implements Operation case SET: // fallthrough on purpose; remove previous Set before setting (ADD) the new one cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange())); case ADD: - doAdd(cf, builder, validator, params); + doAdd(cf, builder, (CollectionType)validator, params); break; case DISCARD: - doDiscard(cf, builder, validator, params); + doDiscard(cf, builder, (CollectionType)validator, params); break; default: throw new AssertionError("Unsupported Set operation: " + kind); } } - public void execute(ColumnFamily cf, ColumnNameBuilder builder, AbstractType validator, UpdateParameters params) throws InvalidRequestException + public static void doSetFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, SetType validator, Term values, UpdateParameters params) throws InvalidRequestException { - throw new InvalidRequestException("Set operations are only supported on Set typed columns, but " + validator + " given."); + if (!values.isBindMarker()) + throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); + + cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange())); + doAddFromPrepared(cf, builder, validator, values, params); } - public static void doInsertFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, SetType validator, Term values, UpdateParameters params) throws InvalidRequestException + public static void doAddFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, SetType validator, Term values, UpdateParameters params) throws InvalidRequestException { if (!values.isBindMarker()) throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); - cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange())); - try { Set s = validator.compose(params.variables.get(values.bindIndex)); @@ -101,6 +103,28 @@ public class SetOperation implements Operation } } + public static void doDiscardFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, SetType validator, Term values, UpdateParameters params) throws InvalidRequestException + { + if (!values.isBindMarker()) + throw new InvalidRequestException("Can't apply operation on column with " + validator + " type."); + + try + { + Set s = validator.compose(params.variables.get(values.bindIndex)); + Iterator iter = s.iterator(); + while (iter.hasNext()) + { + ColumnNameBuilder b = iter.hasNext() ? builder.copy() : builder; + ByteBuffer name = b.add(validator.nameComparator().decompose(iter.next())).build(); + cf.addColumn(params.makeTombstone(name)); + } + } + catch (MarshalException e) + { + throw new InvalidRequestException(e.getMessage()); + } + } + private void doAdd(ColumnFamily cf, ColumnNameBuilder builder, CollectionType validator, UpdateParameters params) throws InvalidRequestException { for (int i = 0; i < values.size(); ++i) @@ -126,7 +150,7 @@ public class SetOperation implements Operation return values; } - public boolean requiresRead() + public boolean requiresRead(AbstractType validator) { return false; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java index 915e8ed..97844ad 100644 --- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java @@ -122,7 +122,7 @@ public class UpdateStatement extends ModificationStatement if (!(name.type instanceof ListType)) continue; - if (value.requiresRead()) + if (value.requiresRead(name.type)) { if (toRead == null) toRead = new TreeSet(UTF8Type.instance); @@ -254,7 +254,7 @@ public class UpdateStatement extends ModificationStatement { CFDefinition.Name name = entry.getKey(); Operation op = entry.getValue(); - hasCounterColumn |= addToMutation(cf, builder.copy().add(name.name.key), name, op, params, group == null || !op.requiresRead() ? null : group.getCollection(name.name.key)); + hasCounterColumn |= addToMutation(cf, builder.copy().add(name.name.key), name, op, params, group == null || !op.requiresRead(name.type) ? null : group.getCollection(name.name.key)); } } @@ -272,31 +272,18 @@ public class UpdateStatement extends ModificationStatement switch (type) { - case COLUMN: - if (valueDef != null && valueDef.type.isCollection()) - { - // This means this was a prepared statement where the whole collection was provided - // For have to deserialize it since it will be multiple columns - ((ColumnOperation)valueOperation).executePreparedCollection(cf, builder.copy(), (CollectionType)valueDef.type, params); - } - else - { - valueOperation.execute(cf, builder.copy(), valueDef == null ? null : valueDef.type, params); - } - break; case COUNTER: if (valueDef != null && valueDef.type.isCollection()) throw new InvalidRequestException("Cannot assign collection value to column with " + valueDef.type + " type."); - - valueOperation.execute(cf, builder.copy(), valueDef == null ? null : valueDef.type, params); break; - default: + case LIST: + case SET: + case MAP: if (!valueDef.type.isCollection()) throw new InvalidRequestException("Can't apply collection operation on column with " + valueDef.type + " type."); - - valueOperation.execute(cf, builder.copy(), (CollectionType) valueDef.type, params, list); break; } + valueOperation.execute(cf, builder.copy(), valueDef == null ? null : valueDef.type, params, list); return valueOperation.getType() == Operation.Type.COUNTER; }